Add NonNull annotations to metadata packages

Also remove MetadataRenderer and SpliceInfoDecoder from the
nullness blacklist

PiperOrigin-RevId: 283744417
This commit is contained in:
olly 2019-12-04 14:28:18 +00:00 committed by Ian Baker
parent b7666df2b3
commit 3930a539e0
12 changed files with 158 additions and 36 deletions

View File

@ -22,7 +22,6 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/** /**
* A collection of metadata entries. * A collection of metadata entries.
@ -57,19 +56,15 @@ public final class Metadata implements Parcelable {
* @param entries The metadata entries. * @param entries The metadata entries.
*/ */
public Metadata(Entry... entries) { public Metadata(Entry... entries) {
this.entries = entries == null ? new Entry[0] : entries; this.entries = entries;
} }
/** /**
* @param entries The metadata entries. * @param entries The metadata entries.
*/ */
public Metadata(List<? extends Entry> entries) { public Metadata(List<? extends Entry> entries) {
if (entries != null) {
this.entries = new Entry[entries.size()]; this.entries = new Entry[entries.size()];
entries.toArray(this.entries); entries.toArray(this.entries);
} else {
this.entries = new Entry[0];
}
} }
/* package */ Metadata(Parcel in) { /* package */ Metadata(Parcel in) {
@ -118,9 +113,10 @@ public final class Metadata implements Parcelable {
* @return The metadata instance with the appended entries. * @return The metadata instance with the appended entries.
*/ */
public Metadata copyWithAppendedEntries(Entry... entriesToAppend) { public Metadata copyWithAppendedEntries(Entry... entriesToAppend) {
@NullableType Entry[] merged = Arrays.copyOf(entries, entries.length + entriesToAppend.length); if (entriesToAppend.length == 0) {
System.arraycopy(entriesToAppend, 0, merged, entries.length, entriesToAppend.length); return this;
return new Metadata(Util.castNonNullTypeArray(merged)); }
return new Metadata(Util.nullSafeArrayConcatenation(entries, entriesToAppend));
} }
@Override @Override

View File

@ -15,6 +15,8 @@
*/ */
package com.google.android.exoplayer2.metadata; package com.google.android.exoplayer2.metadata;
import static com.google.android.exoplayer2.util.Util.castNonNull;
import android.os.Handler; import android.os.Handler;
import android.os.Handler.Callback; import android.os.Handler.Callback;
import android.os.Looper; import android.os.Looper;
@ -22,7 +24,6 @@ import android.os.Message;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.BaseRenderer; import com.google.android.exoplayer2.BaseRenderer;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
@ -30,6 +31,7 @@ import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/** /**
* A renderer for metadata. * A renderer for metadata.
@ -46,12 +48,12 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
private final MetadataOutput output; private final MetadataOutput output;
@Nullable private final Handler outputHandler; @Nullable private final Handler outputHandler;
private final MetadataInputBuffer buffer; private final MetadataInputBuffer buffer;
private final Metadata[] pendingMetadata; private final @NullableType Metadata[] pendingMetadata;
private final long[] pendingMetadataTimestamps; private final long[] pendingMetadataTimestamps;
private int pendingMetadataIndex; private int pendingMetadataIndex;
private int pendingMetadataCount; private int pendingMetadataCount;
private MetadataDecoder decoder; @Nullable private MetadataDecoder decoder;
private boolean inputStreamEnded; private boolean inputStreamEnded;
private long subsampleOffsetUs; private long subsampleOffsetUs;
@ -98,7 +100,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
} }
@Override @Override
protected void onStreamChanged(Format[] formats, long offsetUs) throws ExoPlaybackException { protected void onStreamChanged(Format[] formats, long offsetUs) {
decoder = decoderFactory.createDecoder(formats[0]); decoder = decoderFactory.createDecoder(formats[0]);
} }
@ -109,7 +111,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
} }
@Override @Override
public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { public void render(long positionUs, long elapsedRealtimeUs) {
if (!inputStreamEnded && pendingMetadataCount < MAX_PENDING_METADATA_COUNT) { if (!inputStreamEnded && pendingMetadataCount < MAX_PENDING_METADATA_COUNT) {
buffer.clear(); buffer.clear();
FormatHolder formatHolder = getFormatHolder(); FormatHolder formatHolder = getFormatHolder();
@ -124,7 +126,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
} else { } else {
buffer.subsampleOffsetUs = subsampleOffsetUs; buffer.subsampleOffsetUs = subsampleOffsetUs;
buffer.flip(); buffer.flip();
Metadata metadata = decoder.decode(buffer); @Nullable Metadata metadata = castNonNull(decoder).decode(buffer);
if (metadata != null) { if (metadata != null) {
List<Metadata.Entry> entries = new ArrayList<>(metadata.length()); List<Metadata.Entry> entries = new ArrayList<>(metadata.length());
decodeWrappedMetadata(metadata, entries); decodeWrappedMetadata(metadata, entries);
@ -139,12 +141,13 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
} }
} }
} else if (result == C.RESULT_FORMAT_READ) { } else if (result == C.RESULT_FORMAT_READ) {
subsampleOffsetUs = formatHolder.format.subsampleOffsetUs; subsampleOffsetUs = Assertions.checkNotNull(formatHolder.format).subsampleOffsetUs;
} }
} }
if (pendingMetadataCount > 0 && pendingMetadataTimestamps[pendingMetadataIndex] <= positionUs) { if (pendingMetadataCount > 0 && pendingMetadataTimestamps[pendingMetadataIndex] <= positionUs) {
invokeRenderer(pendingMetadata[pendingMetadataIndex]); Metadata metadata = castNonNull(pendingMetadata[pendingMetadataIndex]);
invokeRenderer(metadata);
pendingMetadata[pendingMetadataIndex] = null; pendingMetadata[pendingMetadataIndex] = null;
pendingMetadataIndex = (pendingMetadataIndex + 1) % MAX_PENDING_METADATA_COUNT; pendingMetadataIndex = (pendingMetadataIndex + 1) % MAX_PENDING_METADATA_COUNT;
pendingMetadataCount--; pendingMetadataCount--;
@ -158,7 +161,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
*/ */
private void decodeWrappedMetadata(Metadata metadata, List<Metadata.Entry> decodedEntries) { private void decodeWrappedMetadata(Metadata metadata, List<Metadata.Entry> decodedEntries) {
for (int i = 0; i < metadata.length(); i++) { for (int i = 0; i < metadata.length(); i++) {
Format wrappedMetadataFormat = metadata.get(i).getWrappedMetadataFormat(); @Nullable Format wrappedMetadataFormat = metadata.get(i).getWrappedMetadataFormat();
if (wrappedMetadataFormat != null && decoderFactory.supportsFormat(wrappedMetadataFormat)) { if (wrappedMetadataFormat != null && decoderFactory.supportsFormat(wrappedMetadataFormat)) {
MetadataDecoder wrappedMetadataDecoder = MetadataDecoder wrappedMetadataDecoder =
decoderFactory.createDecoder(wrappedMetadataFormat); decoderFactory.createDecoder(wrappedMetadataFormat);
@ -167,7 +170,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
Assertions.checkNotNull(metadata.get(i).getWrappedMetadataBytes()); Assertions.checkNotNull(metadata.get(i).getWrappedMetadataBytes());
buffer.clear(); buffer.clear();
buffer.ensureSpaceForWrite(wrappedMetadataBytes.length); buffer.ensureSpaceForWrite(wrappedMetadataBytes.length);
buffer.data.put(wrappedMetadataBytes); castNonNull(buffer.data).put(wrappedMetadataBytes);
buffer.flip(); buffer.flip();
@Nullable Metadata innerMetadata = wrappedMetadataDecoder.decode(buffer); @Nullable Metadata innerMetadata = wrappedMetadataDecoder.decode(buffer);
if (innerMetadata != null) { if (innerMetadata != null) {

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2019 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.
*/
@NonNullApi
package com.google.android.exoplayer2.metadata.emsg;
import com.google.android.exoplayer2.util.NonNullApi;

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2019 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.
*/
@NonNullApi
package com.google.android.exoplayer2.metadata.flac;
import com.google.android.exoplayer2.util.NonNullApi;

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.metadata.icy; package com.google.android.exoplayer2.metadata.icy;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.MetadataDecoder; import com.google.android.exoplayer2.metadata.MetadataDecoder;
@ -28,8 +29,6 @@ import java.util.regex.Pattern;
/** Decodes ICY stream information. */ /** Decodes ICY stream information. */
public final class IcyDecoder implements MetadataDecoder { public final class IcyDecoder implements MetadataDecoder {
private static final String TAG = "IcyDecoder";
private static final Pattern METADATA_ELEMENT = Pattern.compile("(.+?)='(.*?)';", Pattern.DOTALL); private static final Pattern METADATA_ELEMENT = Pattern.compile("(.+?)='(.*?)';", Pattern.DOTALL);
private static final String STREAM_KEY_NAME = "streamtitle"; private static final String STREAM_KEY_NAME = "streamtitle";
private static final String STREAM_KEY_URL = "streamurl"; private static final String STREAM_KEY_URL = "streamurl";
@ -45,8 +44,8 @@ public final class IcyDecoder implements MetadataDecoder {
@VisibleForTesting @VisibleForTesting
/* package */ Metadata decode(String metadata) { /* package */ Metadata decode(String metadata) {
String name = null; @Nullable String name = null;
String url = null; @Nullable String url = null;
int index = 0; int index = 0;
Matcher matcher = METADATA_ELEMENT.matcher(metadata); Matcher matcher = METADATA_ELEMENT.matcher(metadata);
while (matcher.find(index)) { while (matcher.find(index)) {

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2019 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.
*/
@NonNullApi
package com.google.android.exoplayer2.metadata.icy;
import com.google.android.exoplayer2.util.NonNullApi;

View File

@ -47,7 +47,7 @@ public final class ApicFrame extends Id3Frame {
/* package */ ApicFrame(Parcel in) { /* package */ ApicFrame(Parcel in) {
super(ID); super(ID);
mimeType = castNonNull(in.readString()); mimeType = castNonNull(in.readString());
description = castNonNull(in.readString()); description = in.readString();
pictureType = in.readInt(); pictureType = in.readInt();
pictureData = castNonNull(in.createByteArray()); pictureData = castNonNull(in.createByteArray());
} }

View File

@ -155,7 +155,8 @@ public final class Id3Decoder implements MetadataDecoder {
* @param data A {@link ParsableByteArray} from which the header should be read. * @param data A {@link ParsableByteArray} from which the header should be read.
* @return The parsed header, or null if the ID3 tag is unsupported. * @return The parsed header, or null if the ID3 tag is unsupported.
*/ */
private static @Nullable Id3Header decodeHeader(ParsableByteArray data) { @Nullable
private static Id3Header decodeHeader(ParsableByteArray data) {
if (data.bytesLeft() < ID3_HEADER_LENGTH) { if (data.bytesLeft() < ID3_HEADER_LENGTH) {
Log.w(TAG, "Data too short to be an ID3 tag"); Log.w(TAG, "Data too short to be an ID3 tag");
return null; return null;
@ -269,7 +270,8 @@ public final class Id3Decoder implements MetadataDecoder {
} }
} }
private static @Nullable Id3Frame decodeFrame( @Nullable
private static Id3Frame decodeFrame(
int majorVersion, int majorVersion,
ParsableByteArray id3Data, ParsableByteArray id3Data,
boolean unsignedIntFrameSizeHack, boolean unsignedIntFrameSizeHack,
@ -404,8 +406,9 @@ public final class Id3Decoder implements MetadataDecoder {
} }
} }
private static @Nullable TextInformationFrame decodeTxxxFrame( @Nullable
ParsableByteArray id3Data, int frameSize) throws UnsupportedEncodingException { private static TextInformationFrame decodeTxxxFrame(ParsableByteArray id3Data, int frameSize)
throws UnsupportedEncodingException {
if (frameSize < 1) { if (frameSize < 1) {
// Frame is malformed. // Frame is malformed.
return null; return null;
@ -427,7 +430,8 @@ public final class Id3Decoder implements MetadataDecoder {
return new TextInformationFrame("TXXX", description, value); return new TextInformationFrame("TXXX", description, value);
} }
private static @Nullable TextInformationFrame decodeTextInformationFrame( @Nullable
private static TextInformationFrame decodeTextInformationFrame(
ParsableByteArray id3Data, int frameSize, String id) throws UnsupportedEncodingException { ParsableByteArray id3Data, int frameSize, String id) throws UnsupportedEncodingException {
if (frameSize < 1) { if (frameSize < 1) {
// Frame is malformed. // Frame is malformed.
@ -446,7 +450,8 @@ public final class Id3Decoder implements MetadataDecoder {
return new TextInformationFrame(id, null, value); return new TextInformationFrame(id, null, value);
} }
private static @Nullable UrlLinkFrame decodeWxxxFrame(ParsableByteArray id3Data, int frameSize) @Nullable
private static UrlLinkFrame decodeWxxxFrame(ParsableByteArray id3Data, int frameSize)
throws UnsupportedEncodingException { throws UnsupportedEncodingException {
if (frameSize < 1) { if (frameSize < 1) {
// Frame is malformed. // Frame is malformed.
@ -557,7 +562,8 @@ public final class Id3Decoder implements MetadataDecoder {
return new ApicFrame(mimeType, description, pictureType, pictureData); return new ApicFrame(mimeType, description, pictureType, pictureData);
} }
private static @Nullable CommentFrame decodeCommentFrame(ParsableByteArray id3Data, int frameSize) @Nullable
private static CommentFrame decodeCommentFrame(ParsableByteArray id3Data, int frameSize)
throws UnsupportedEncodingException { throws UnsupportedEncodingException {
if (frameSize < 4) { if (frameSize < 4) {
// Frame is malformed. // Frame is malformed.

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2019 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.
*/
@NonNullApi
package com.google.android.exoplayer2.metadata.id3;
import com.google.android.exoplayer2.util.NonNullApi;

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2019 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.
*/
@NonNullApi
package com.google.android.exoplayer2.metadata;
import com.google.android.exoplayer2.util.NonNullApi;

View File

@ -15,13 +15,16 @@
*/ */
package com.google.android.exoplayer2.metadata.scte35; package com.google.android.exoplayer2.metadata.scte35;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.MetadataDecoder; import com.google.android.exoplayer2.metadata.MetadataDecoder;
import com.google.android.exoplayer2.metadata.MetadataInputBuffer; import com.google.android.exoplayer2.metadata.MetadataInputBuffer;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.TimestampAdjuster; import com.google.android.exoplayer2.util.TimestampAdjuster;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /**
* Decodes splice info sections and produces splice commands. * Decodes splice info sections and produces splice commands.
@ -37,7 +40,7 @@ public final class SpliceInfoDecoder implements MetadataDecoder {
private final ParsableByteArray sectionData; private final ParsableByteArray sectionData;
private final ParsableBitArray sectionHeader; private final ParsableBitArray sectionHeader;
private TimestampAdjuster timestampAdjuster; @MonotonicNonNull private TimestampAdjuster timestampAdjuster;
public SpliceInfoDecoder() { public SpliceInfoDecoder() {
sectionData = new ParsableByteArray(); sectionData = new ParsableByteArray();
@ -47,6 +50,8 @@ public final class SpliceInfoDecoder implements MetadataDecoder {
@SuppressWarnings("ByteBufferBackingArray") @SuppressWarnings("ByteBufferBackingArray")
@Override @Override
public Metadata decode(MetadataInputBuffer inputBuffer) { public Metadata decode(MetadataInputBuffer inputBuffer) {
ByteBuffer buffer = Assertions.checkNotNull(inputBuffer.data);
// Internal timestamps adjustment. // Internal timestamps adjustment.
if (timestampAdjuster == null if (timestampAdjuster == null
|| inputBuffer.subsampleOffsetUs != timestampAdjuster.getTimestampOffsetUs()) { || inputBuffer.subsampleOffsetUs != timestampAdjuster.getTimestampOffsetUs()) {
@ -54,7 +59,6 @@ public final class SpliceInfoDecoder implements MetadataDecoder {
timestampAdjuster.adjustSampleTimestamp(inputBuffer.timeUs - inputBuffer.subsampleOffsetUs); timestampAdjuster.adjustSampleTimestamp(inputBuffer.timeUs - inputBuffer.subsampleOffsetUs);
} }
ByteBuffer buffer = inputBuffer.data;
byte[] data = buffer.array(); byte[] data = buffer.array();
int size = buffer.limit(); int size = buffer.limit();
sectionData.reset(data, size); sectionData.reset(data, size);
@ -68,7 +72,7 @@ public final class SpliceInfoDecoder implements MetadataDecoder {
sectionHeader.skipBits(20); sectionHeader.skipBits(20);
int spliceCommandLength = sectionHeader.readBits(12); int spliceCommandLength = sectionHeader.readBits(12);
int spliceCommandType = sectionHeader.readBits(8); int spliceCommandType = sectionHeader.readBits(8);
SpliceCommand command = null; @Nullable SpliceCommand command = null;
// Go to the start of the command by skipping all fields up to command_type. // Go to the start of the command by skipping all fields up to command_type.
sectionData.skipBytes(14); sectionData.skipBytes(14);
switch (spliceCommandType) { switch (spliceCommandType) {

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2019 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.
*/
@NonNullApi
package com.google.android.exoplayer2.metadata.scte35;
import com.google.android.exoplayer2.util.NonNullApi;