Remove CEA package from null-checking blacklist

PiperOrigin-RevId: 290610312
This commit is contained in:
ibaker 2020-01-20 14:52:26 +00:00 committed by Ian Baker
parent de3f04b721
commit f7470c5126
3 changed files with 49 additions and 29 deletions

View file

@ -24,17 +24,21 @@ import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.UnderlineSpan;
import androidx.annotation.Nullable;
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.text.SubtitleDecoder;
import com.google.android.exoplayer2.text.SubtitleInputBuffer;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/**
* A {@link SubtitleDecoder} for CEA-608 (also known as "line 21 captions" and "EIA-608").
@ -236,8 +240,8 @@ public final class Cea608Decoder extends CeaDecoder {
private final ArrayList<CueBuilder> cueBuilders;
private CueBuilder currentCueBuilder;
private List<Cue> cues;
private List<Cue> lastCues;
@Nullable private List<Cue> cues;
@Nullable private List<Cue> lastCues;
private int captionMode;
private int captionRowCount;
@ -321,13 +325,14 @@ public final class Cea608Decoder extends CeaDecoder {
@Override
protected Subtitle createSubtitle() {
lastCues = cues;
return new CeaSubtitle(cues);
return new CeaSubtitle(Assertions.checkNotNull(cues));
}
@SuppressWarnings("ByteBufferBackingArray")
@Override
protected void decode(SubtitleInputBuffer inputBuffer) {
ccData.reset(inputBuffer.data.array(), inputBuffer.data.limit());
ByteBuffer subtitleData = Assertions.checkNotNull(inputBuffer.data);
ccData.reset(subtitleData.array(), subtitleData.limit());
boolean captionDataProcessed = false;
while (ccData.bytesLeft() >= packetLength) {
byte ccHeader = packetLength == 2 ? CC_IMPLICIT_DATA_HEADER
@ -572,9 +577,9 @@ public final class Cea608Decoder extends CeaDecoder {
// preference, then middle alignment, then end alignment.
@Cue.AnchorType int positionAnchor = Cue.ANCHOR_TYPE_END;
int cueBuilderCount = cueBuilders.size();
List<Cue> cueBuilderCues = new ArrayList<>(cueBuilderCount);
List<@NullableType Cue> cueBuilderCues = new ArrayList<>(cueBuilderCount);
for (int i = 0; i < cueBuilderCount; i++) {
Cue cue = cueBuilders.get(i).build(/* forcedPositionAnchor= */ Cue.TYPE_UNSET);
@Nullable Cue cue = cueBuilders.get(i).build(/* forcedPositionAnchor= */ Cue.TYPE_UNSET);
cueBuilderCues.add(cue);
if (cue != null) {
positionAnchor = Math.min(positionAnchor, cue.positionAnchor);
@ -584,10 +589,11 @@ public final class Cea608Decoder extends CeaDecoder {
// Skip null cues and rebuild any that don't have the preferred alignment.
List<Cue> displayCues = new ArrayList<>(cueBuilderCount);
for (int i = 0; i < cueBuilderCount; i++) {
Cue cue = cueBuilderCues.get(i);
@Nullable Cue cue = cueBuilderCues.get(i);
if (cue != null) {
if (cue.positionAnchor != positionAnchor) {
cue = cueBuilders.get(i).build(positionAnchor);
// The last time we built this cue it was non-null, it will be non-null this time too.
cue = Assertions.checkNotNull(cueBuilders.get(i).build(positionAnchor));
}
displayCues.add(cue);
}
@ -745,7 +751,7 @@ public final class Cea608Decoder extends CeaDecoder {
return (cc1 & 0xF7) == 0x14;
}
private static class CueBuilder {
private static final class CueBuilder {
// 608 captions define a 15 row by 32 column screen grid. These constants convert from 608
// positions to normalized screen position.
@ -767,7 +773,7 @@ public final class Cea608Decoder extends CeaDecoder {
rolledUpCaptions = new ArrayList<>();
captionStringBuilder = new StringBuilder();
reset(captionMode);
setCaptionRowCount(captionRowCount);
this.captionRowCount = captionRowCount;
}
public void reset(int captionMode) {
@ -829,6 +835,7 @@ public final class Cea608Decoder extends CeaDecoder {
}
}
@Nullable
public Cue build(@Cue.AnchorType int forcedPositionAnchor) {
SpannableStringBuilder cueString = new SpannableStringBuilder();
// Add any rolled up captions, separated by new lines.

View file

@ -37,9 +37,11 @@ import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/**
* A {@link SubtitleDecoder} for CEA-708 (also known as "EIA-708").
@ -147,10 +149,10 @@ public final class Cea708Decoder extends CeaDecoder {
private final CueInfoBuilder[] cueInfoBuilders;
private CueInfoBuilder currentCueInfoBuilder;
private List<Cue> cues;
private List<Cue> lastCues;
@Nullable private List<Cue> cues;
@Nullable private List<Cue> lastCues;
private DtvCcPacket currentDtvCcPacket;
@Nullable private DtvCcPacket currentDtvCcPacket;
private int currentWindow;
// TODO: Retrieve isWideAspectRatio from initializationData and use it.
@ -165,7 +167,6 @@ public final class Cea708Decoder extends CeaDecoder {
}
currentCueInfoBuilder = cueInfoBuilders[0];
resetCueBuilders();
}
@Override
@ -192,15 +193,16 @@ public final class Cea708Decoder extends CeaDecoder {
@Override
protected Subtitle createSubtitle() {
lastCues = cues;
return new CeaSubtitle(cues);
return new CeaSubtitle(Assertions.checkNotNull(cues));
}
@Override
protected void decode(SubtitleInputBuffer inputBuffer) {
// Subtitle input buffers are non-direct and the position is zero, so calling array() is safe.
ByteBuffer subtitleData = Assertions.checkNotNull(inputBuffer.data);
@SuppressWarnings("ByteBufferBackingArray")
byte[] inputBufferData = inputBuffer.data.array();
ccData.reset(inputBufferData, inputBuffer.data.limit());
byte[] inputBufferData = subtitleData.array();
ccData.reset(inputBufferData, subtitleData.limit());
while (ccData.bytesLeft() >= 3) {
int ccTypeAndValid = (ccData.readUnsignedByte() & 0x07);
@ -259,6 +261,7 @@ public final class Cea708Decoder extends CeaDecoder {
currentDtvCcPacket = null;
}
@RequiresNonNull("currentDtvCcPacket")
private void processCurrentPacket() {
if (currentDtvCcPacket.currentIndex != (currentDtvCcPacket.packetSize * 2 - 1)) {
Log.w(TAG, "DtvCcPacket ended prematurely; size is " + (currentDtvCcPacket.packetSize * 2 - 1)
@ -761,7 +764,10 @@ public final class Cea708Decoder extends CeaDecoder {
List<Cea708CueInfo> displayCueInfos = new ArrayList<>();
for (int i = 0; i < NUM_WINDOWS; i++) {
if (!cueInfoBuilders[i].isEmpty() && cueInfoBuilders[i].isVisible()) {
displayCueInfos.add(cueInfoBuilders[i].build());
@Nullable Cea708CueInfo cueInfo = cueInfoBuilders[i].build();
if (cueInfo != null) {
displayCueInfos.add(cueInfo);
}
}
}
Collections.sort(
@ -1157,6 +1163,7 @@ public final class Cea708Decoder extends CeaDecoder {
return new SpannableString(spannableStringBuilder);
}
@Nullable
public Cea708CueInfo build() {
if (isEmpty()) {
// The cue is empty.

View file

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.text.cea;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.text.Subtitle;
@ -24,6 +25,7 @@ import com.google.android.exoplayer2.text.SubtitleDecoderException;
import com.google.android.exoplayer2.text.SubtitleInputBuffer;
import com.google.android.exoplayer2.text.SubtitleOutputBuffer;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.util.ArrayDeque;
import java.util.PriorityQueue;
@ -39,7 +41,7 @@ import java.util.PriorityQueue;
private final ArrayDeque<SubtitleOutputBuffer> availableOutputBuffers;
private final PriorityQueue<CeaInputBuffer> queuedInputBuffers;
private CeaInputBuffer dequeuedInputBuffer;
@Nullable private CeaInputBuffer dequeuedInputBuffer;
private long playbackPositionUs;
private long queuedInputBufferCount;
@ -64,6 +66,7 @@ import java.util.PriorityQueue;
}
@Override
@Nullable
public SubtitleInputBuffer dequeueInputBuffer() throws SubtitleDecoderException {
Assertions.checkState(dequeuedInputBuffer == null);
if (availableInputBuffers.isEmpty()) {
@ -76,18 +79,20 @@ import java.util.PriorityQueue;
@Override
public void queueInputBuffer(SubtitleInputBuffer inputBuffer) throws SubtitleDecoderException {
Assertions.checkArgument(inputBuffer == dequeuedInputBuffer);
if (inputBuffer.isDecodeOnly()) {
CeaInputBuffer ceaInputBuffer = (CeaInputBuffer) inputBuffer;
if (ceaInputBuffer.isDecodeOnly()) {
// We can drop this buffer early (i.e. before it would be decoded) as the CEA formats allow
// for decoding to begin mid-stream.
releaseInputBuffer(dequeuedInputBuffer);
releaseInputBuffer(ceaInputBuffer);
} else {
dequeuedInputBuffer.queuedInputBufferCount = queuedInputBufferCount++;
queuedInputBuffers.add(dequeuedInputBuffer);
ceaInputBuffer.queuedInputBufferCount = queuedInputBufferCount++;
queuedInputBuffers.add(ceaInputBuffer);
}
dequeuedInputBuffer = null;
}
@Override
@Nullable
public SubtitleOutputBuffer dequeueOutputBuffer() throws SubtitleDecoderException {
if (availableOutputBuffers.isEmpty()) {
return null;
@ -96,13 +101,14 @@ import java.util.PriorityQueue;
// to the current playback position; processing input buffers for future content should
// be deferred until they would be applicable
while (!queuedInputBuffers.isEmpty()
&& queuedInputBuffers.peek().timeUs <= playbackPositionUs) {
CeaInputBuffer inputBuffer = queuedInputBuffers.poll();
&& Util.castNonNull(queuedInputBuffers.peek()).timeUs <= playbackPositionUs) {
CeaInputBuffer inputBuffer = Util.castNonNull(queuedInputBuffers.poll());
// If the input buffer indicates we've reached the end of the stream, we can
// return immediately with an output buffer propagating that
if (inputBuffer.isEndOfStream()) {
SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst();
// availableOutputBuffers.isEmpty() is checked at the top of the method, so this is safe.
SubtitleOutputBuffer outputBuffer = Util.castNonNull(availableOutputBuffers.pollFirst());
outputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
releaseInputBuffer(inputBuffer);
return outputBuffer;
@ -116,7 +122,8 @@ import java.util.PriorityQueue;
// isn't accidentally prepended to the next subtitle
Subtitle subtitle = createSubtitle();
if (!inputBuffer.isDecodeOnly()) {
SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst();
// availableOutputBuffers.isEmpty() is checked at the top of the method, so this is safe.
SubtitleOutputBuffer outputBuffer = Util.castNonNull(availableOutputBuffers.pollFirst());
outputBuffer.setContent(inputBuffer.timeUs, subtitle, Format.OFFSET_SAMPLE_RELATIVE);
releaseInputBuffer(inputBuffer);
return outputBuffer;
@ -125,7 +132,6 @@ import java.util.PriorityQueue;
releaseInputBuffer(inputBuffer);
}
return null;
}
@ -144,7 +150,7 @@ import java.util.PriorityQueue;
queuedInputBufferCount = 0;
playbackPositionUs = 0;
while (!queuedInputBuffers.isEmpty()) {
releaseInputBuffer(queuedInputBuffers.poll());
releaseInputBuffer(Util.castNonNull(queuedInputBuffers.poll()));
}
if (dequeuedInputBuffer != null) {
releaseInputBuffer(dequeuedInputBuffer);