Merge remote-tracking branch 'upstream/dev-v2' into dev-v2

# Conflicts:
#	library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionParameters.java
This commit is contained in:
Yannick RUI 2019-08-12 08:24:10 +02:00
commit 0c4df84eaa
79 changed files with 735 additions and 409 deletions

View file

@ -27,6 +27,10 @@
over other selection parameters.
* Remove `AnalyticsCollector.Factory`. Instances can be created directly and
the `Player` set later using `AnalyticsCollector.setPlayer`.
* Add `allowAudioMixedChannelCountAdaptiveness` parameter to
`DefaultTrackSelector` to allow adaptive selections of audio tracks with
different channel counts
([#6257](https://github.com/google/ExoPlayer/issues/6257)).
### 2.10.4 ###

View file

@ -24,7 +24,6 @@ include modulePrefix + 'library-hls'
include modulePrefix + 'library-smoothstreaming'
include modulePrefix + 'library-ui'
include modulePrefix + 'testutils'
include modulePrefix + 'testutils-robolectric'
include modulePrefix + 'extension-ffmpeg'
include modulePrefix + 'extension-flac'
include modulePrefix + 'extension-gvr'
@ -47,7 +46,6 @@ project(modulePrefix + 'library-hls').projectDir = new File(rootDir, 'library/hl
project(modulePrefix + 'library-smoothstreaming').projectDir = new File(rootDir, 'library/smoothstreaming')
project(modulePrefix + 'library-ui').projectDir = new File(rootDir, 'library/ui')
project(modulePrefix + 'testutils').projectDir = new File(rootDir, 'testutils')
project(modulePrefix + 'testutils-robolectric').projectDir = new File(rootDir, 'testutils_robolectric')
project(modulePrefix + 'extension-ffmpeg').projectDir = new File(rootDir, 'extensions/ffmpeg')
project(modulePrefix + 'extension-flac').projectDir = new File(rootDir, 'extensions/flac')
project(modulePrefix + 'extension-gvr').projectDir = new File(rootDir, 'extensions/gvr')

View file

@ -37,7 +37,8 @@ dependencies {
implementation project(modulePrefix + 'library-ui')
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkVersion
testImplementation project(modulePrefix + 'testutils-robolectric')
testImplementation project(modulePrefix + 'testutils')
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
}
ext {

View file

@ -36,7 +36,8 @@ dependencies {
implementation 'androidx.annotation:annotation:1.1.0'
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
testImplementation project(modulePrefix + 'library')
testImplementation project(modulePrefix + 'testutils-robolectric')
testImplementation project(modulePrefix + 'testutils')
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
}
ext {

View file

@ -40,7 +40,8 @@ dependencies {
implementation project(modulePrefix + 'library-core')
implementation 'androidx.annotation:annotation:1.1.0'
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
testImplementation project(modulePrefix + 'testutils-robolectric')
testImplementation project(modulePrefix + 'testutils')
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
}
ext {

View file

@ -43,7 +43,8 @@ dependencies {
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
androidTestImplementation project(modulePrefix + 'testutils')
androidTestImplementation 'androidx.test:runner:' + androidXTestVersion
testImplementation project(modulePrefix + 'testutils-robolectric')
testImplementation project(modulePrefix + 'testutils')
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
}
ext {

View file

@ -32,11 +32,12 @@ android {
}
dependencies {
api 'com.google.ads.interactivemedia.v3:interactivemedia:3.11.2'
api 'com.google.ads.interactivemedia.v3:interactivemedia:3.11.3'
implementation project(modulePrefix + 'library-core')
implementation 'androidx.annotation:annotation:1.1.0'
implementation 'com.google.android.gms:play-services-ads-identifier:16.0.0'
testImplementation project(modulePrefix + 'testutils-robolectric')
testImplementation project(modulePrefix + 'testutils')
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
}
ext {

View file

@ -40,7 +40,8 @@ android {
dependencies {
implementation project(modulePrefix + 'library-core')
implementation 'androidx.annotation:annotation:1.1.0'
testImplementation project(modulePrefix + 'testutils-robolectric')
testImplementation project(modulePrefix + 'testutils')
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
androidTestImplementation 'androidx.test:runner:' + androidXTestVersion
androidTestImplementation 'androidx.test.ext:junit:' + androidXTestVersion
}

View file

@ -34,7 +34,8 @@ dependencies {
implementation project(modulePrefix + 'library-core')
implementation 'net.butterflytv.utils:rtmp-client:3.0.1'
implementation 'androidx.annotation:annotation:1.1.0'
testImplementation project(modulePrefix + 'testutils-robolectric')
testImplementation project(modulePrefix + 'testutils')
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
}
ext {

View file

@ -40,7 +40,8 @@ android {
dependencies {
implementation project(modulePrefix + 'library-core')
implementation 'androidx.annotation:annotation:1.1.0'
testImplementation project(modulePrefix + 'testutils-robolectric')
testImplementation project(modulePrefix + 'testutils')
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
androidTestImplementation 'androidx.test:runner:' + androidXTestVersion
androidTestImplementation 'androidx.test.ext:junit:' + androidXTestVersion
androidTestImplementation 'com.google.truth:truth:' + truthVersion

View file

@ -174,8 +174,8 @@ public class LibvpxVideoRenderer extends BaseRenderer {
*/
public LibvpxVideoRenderer(
long allowedJoiningTimeMs,
Handler eventHandler,
VideoRendererEventListener eventListener,
@Nullable Handler eventHandler,
@Nullable VideoRendererEventListener eventListener,
int maxDroppedFramesToNotify) {
this(
allowedJoiningTimeMs,
@ -206,10 +206,10 @@ public class LibvpxVideoRenderer extends BaseRenderer {
*/
public LibvpxVideoRenderer(
long allowedJoiningTimeMs,
Handler eventHandler,
VideoRendererEventListener eventListener,
@Nullable Handler eventHandler,
@Nullable VideoRendererEventListener eventListener,
int maxDroppedFramesToNotify,
DrmSessionManager<ExoMediaCrypto> drmSessionManager,
@Nullable DrmSessionManager<ExoMediaCrypto> drmSessionManager,
boolean playClearSamplesWithoutKeys,
boolean disableLoopFilter) {
this(
@ -249,10 +249,10 @@ public class LibvpxVideoRenderer extends BaseRenderer {
*/
public LibvpxVideoRenderer(
long allowedJoiningTimeMs,
Handler eventHandler,
VideoRendererEventListener eventListener,
@Nullable Handler eventHandler,
@Nullable VideoRendererEventListener eventListener,
int maxDroppedFramesToNotify,
DrmSessionManager<ExoMediaCrypto> drmSessionManager,
@Nullable DrmSessionManager<ExoMediaCrypto> drmSessionManager,
boolean playClearSamplesWithoutKeys,
boolean disableLoopFilter,
boolean enableRowMultiThreadMode,
@ -847,7 +847,7 @@ public class LibvpxVideoRenderer extends BaseRenderer {
pendingFormat = null;
}
inputBuffer.flip();
inputBuffer.colorInfo = formatHolder.format.colorInfo;
inputBuffer.colorInfo = format.colorInfo;
onQueueInputBuffer(inputBuffer);
decoder.queueInputBuffer(inputBuffer);
buffersInCodecCount++;

View file

@ -33,7 +33,7 @@ import java.nio.ByteBuffer;
private static final int DECODE_ERROR = 1;
private static final int DRM_ERROR = 2;
private final ExoMediaCrypto exoMediaCrypto;
@Nullable private final ExoMediaCrypto exoMediaCrypto;
private final long vpxDecContext;
@C.VideoOutputMode private volatile int outputMode;
@ -55,7 +55,7 @@ import java.nio.ByteBuffer;
int numInputBuffers,
int numOutputBuffers,
int initialInputBufferSize,
ExoMediaCrypto exoMediaCrypto,
@Nullable ExoMediaCrypto exoMediaCrypto,
boolean disableLoopFilter,
boolean enableRowMultiThreadMode,
int threads)
@ -170,9 +170,19 @@ import java.nio.ByteBuffer;
private native long vpxClose(long context);
private native long vpxDecode(long context, ByteBuffer encoded, int length);
private native long vpxSecureDecode(long context, ByteBuffer encoded, int length,
ExoMediaCrypto mediaCrypto, int inputMode, byte[] key, byte[] iv,
int numSubSamples, int[] numBytesOfClearData, int[] numBytesOfEncryptedData);
private native long vpxSecureDecode(
long context,
ByteBuffer encoded,
int length,
@Nullable ExoMediaCrypto mediaCrypto,
int inputMode,
byte[] key,
byte[] iv,
int numSubSamples,
int[] numBytesOfClearData,
int[] numBytesOfEncryptedData);
private native int vpxGetFrame(long context, VpxOutputBuffer outputBuffer);
/**

View file

@ -17,6 +17,7 @@ package com.google.android.exoplayer2.ext.vp9;
import android.content.Context;
import android.opengl.GLSurfaceView;
import androidx.annotation.Nullable;
import android.util.AttributeSet;
/**
@ -27,10 +28,10 @@ public class VpxVideoSurfaceView extends GLSurfaceView implements VpxOutputBuffe
private final VpxRenderer renderer;
public VpxVideoSurfaceView(Context context) {
this(context, null);
this(context, /* attrs= */ null);
}
public VpxVideoSurfaceView(Context context, AttributeSet attrs) {
public VpxVideoSurfaceView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
renderer = new VpxRenderer();
setPreserveEGLContextOnPause(true);

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.ext.vp9;
import com.google.android.exoplayer2.util.NonNullApi;

View file

@ -42,7 +42,6 @@ android {
}
test {
java.srcDirs += '../../testutils/src/main/java/'
java.srcDirs += '../../testutils_robolectric/src/main/java/'
}
}

View file

@ -52,6 +52,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
private int pendingMetadataCount;
private MetadataDecoder decoder;
private boolean inputStreamEnded;
private long subsampleOffsetUs;
/**
* @param output The output.
@ -120,7 +121,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
// If we ever need to support a metadata format where this is not the case, we'll need to
// pass the buffer to the decoder and discard the output.
} else {
buffer.subsampleOffsetUs = formatHolder.format.subsampleOffsetUs;
buffer.subsampleOffsetUs = subsampleOffsetUs;
buffer.flip();
int index = (pendingMetadataIndex + pendingMetadataCount) % MAX_PENDING_METADATA_COUNT;
Metadata metadata = decoder.decode(buffer);
@ -130,6 +131,8 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
pendingMetadataCount++;
}
}
} else if (result == C.RESULT_FORMAT_READ) {
subsampleOffsetUs = formatHolder.format.subsampleOffsetUs;
}
}

View file

@ -15,43 +15,34 @@
*/
package com.google.android.exoplayer2.metadata.emsg;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.MetadataDecoder;
import com.google.android.exoplayer2.metadata.MetadataInputBuffer;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
import java.nio.ByteBuffer;
import java.util.Arrays;
/** Decodes data encoded by {@link EventMessageEncoder}. */
public final class EventMessageDecoder implements MetadataDecoder {
private static final String TAG = "EventMessageDecoder";
@SuppressWarnings("ByteBufferBackingArray")
@Override
public Metadata decode(MetadataInputBuffer inputBuffer) {
ByteBuffer buffer = inputBuffer.data;
byte[] data = buffer.array();
int size = buffer.limit();
ParsableByteArray emsgData = new ParsableByteArray(data, size);
return new Metadata(decode(new ParsableByteArray(data, size)));
}
public EventMessage decode(ParsableByteArray emsgData) {
String schemeIdUri = Assertions.checkNotNull(emsgData.readNullTerminatedString());
String value = Assertions.checkNotNull(emsgData.readNullTerminatedString());
long timescale = emsgData.readUnsignedInt();
long presentationTimeDelta = emsgData.readUnsignedInt();
if (presentationTimeDelta != 0) {
// We expect the source to have accounted for presentation_time_delta by adjusting the sample
// timestamp and zeroing the field in the sample data. Log a warning if the field is non-zero.
Log.w(TAG, "Ignoring non-zero presentation_time_delta: " + presentationTimeDelta);
}
long durationMs =
Util.scaleLargeTimestamp(emsgData.readUnsignedInt(), C.MILLIS_PER_SECOND, timescale);
long durationMs = emsgData.readUnsignedInt();
long id = emsgData.readUnsignedInt();
byte[] messageData = Arrays.copyOfRange(data, emsgData.getPosition(), size);
return new Metadata(new EventMessage(schemeIdUri, value, durationMs, id, messageData));
byte[] messageData =
Arrays.copyOfRange(emsgData.data, emsgData.getPosition(), emsgData.limit());
return new EventMessage(schemeIdUri, value, durationMs, id, messageData);
}
}

View file

@ -15,7 +15,6 @@
*/
package com.google.android.exoplayer2.metadata.emsg;
import androidx.annotation.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
@ -40,15 +39,12 @@ public final class EventMessageEncoder {
* @param eventMessage The event message to be encoded.
* @return The serialized byte array.
*/
@Nullable
public byte[] encode(EventMessage eventMessage) {
byteArrayOutputStream.reset();
try {
writeNullTerminatedString(dataOutputStream, eventMessage.schemeIdUri);
String nonNullValue = eventMessage.value != null ? eventMessage.value : "";
writeNullTerminatedString(dataOutputStream, nonNullValue);
writeUnsignedInt(dataOutputStream, 1000); // timescale
writeUnsignedInt(dataOutputStream, 0); // presentation_time_delta
writeUnsignedInt(dataOutputStream, eventMessage.durationMs);
writeUnsignedInt(dataOutputStream, eventMessage.id);
dataOutputStream.write(eventMessage.messageData);

View file

@ -113,15 +113,17 @@ public interface MediaPeriod extends SequenceableLoader {
* corresponding flag in {@code streamResetFlags} will be set to true. This flag will also be set
* if a new sample stream is created.
*
* <p>Note that previously received {@link TrackSelection TrackSelections} are no longer valid and
* references need to be replaced even if the corresponding {@link SampleStream} is kept.
* <p>Note that previously passed {@link TrackSelection TrackSelections} are no longer valid, and
* any references to them must be updated to point to the new selections.
*
* <p>This method is only called after the period has been prepared.
*
* @param selections The renderer track selections.
* @param mayRetainStreamFlags Flags indicating whether the existing sample stream can be retained
* for each selection. A {@code true} value indicates that the selection is unchanged, and
* that the caller does not require that the sample stream be recreated.
* for each track selection. A {@code true} value indicates that the selection is equivalent
* to the one that was previously passed, and that the caller does not require that the sample
* stream be recreated. If a retained sample stream holds any references to the track
* selection then they must be updated to point to the new selection.
* @param streams The existing sample streams, which will be updated to reflect the provided
* selections.
* @param streamResetFlags Will be updated to indicate new sample streams, and sample streams that

View file

@ -177,6 +177,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
private boolean exceedAudioConstraintsIfNecessary;
private boolean allowAudioMixedMimeTypeAdaptiveness;
private boolean allowAudioMixedSampleRateAdaptiveness;
private boolean allowAudioMixedChannelCountAdaptiveness;
// General
private boolean forceLowestBitrate;
private boolean forceHighestSupportedBitrate;
@ -227,6 +228,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
exceedAudioConstraintsIfNecessary = initialValues.exceedAudioConstraintsIfNecessary;
allowAudioMixedMimeTypeAdaptiveness = initialValues.allowAudioMixedMimeTypeAdaptiveness;
allowAudioMixedSampleRateAdaptiveness = initialValues.allowAudioMixedSampleRateAdaptiveness;
allowAudioMixedChannelCountAdaptiveness =
initialValues.allowAudioMixedChannelCountAdaptiveness;
// General
forceLowestBitrate = initialValues.forceLowestBitrate;
forceHighestSupportedBitrate = initialValues.forceHighestSupportedBitrate;
@ -258,8 +261,10 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/**
* See {@link Parameters#maxVideoWidth} and {@link Parameters#maxVideoHeight}.
* Sets the maximum allowed video width and height.
*
* @param maxVideoWidth Maximum allowed video width in pixels.
* @param maxVideoHeight Maximum allowed video height in pixels.
* @return This builder.
*/
public ParametersBuilder setMaxVideoSize(int maxVideoWidth, int maxVideoHeight) {
@ -269,8 +274,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/**
* See {@link Parameters#maxVideoFrameRate}.
* Sets the maximum allowed video frame rate.
*
* @param maxVideoFrameRate Maximum allowed video frame rate in hertz.
* @return This builder.
*/
public ParametersBuilder setMaxVideoFrameRate(int maxVideoFrameRate) {
@ -279,8 +285,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/**
* See {@link Parameters#maxVideoBitrate}.
* Sets the maximum allowed video bitrate.
*
* @param maxVideoBitrate Maximum allowed video bitrate in bits per second.
* @return This builder.
*/
public ParametersBuilder setMaxVideoBitrate(int maxVideoBitrate) {
@ -289,8 +296,11 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/**
* See {@link Parameters#exceedVideoConstraintsIfNecessary}.
* Sets whether to exceed the {@link #setMaxVideoSize(int, int)} and {@link
* #setMaxAudioBitrate(int)} constraints when no selection can be made otherwise.
*
* @param exceedVideoConstraintsIfNecessary Whether to exceed video constraints when no
* selection can be made otherwise.
* @return This builder.
*/
public ParametersBuilder setExceedVideoConstraintsIfNecessary(
@ -300,8 +310,14 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/**
* See {@link Parameters#allowVideoMixedMimeTypeAdaptiveness}.
* Sets whether to allow adaptive video selections containing mixed MIME types.
*
* <p>Adaptations between different MIME types may not be completely seamless, in which case
* {@link #setAllowVideoNonSeamlessAdaptiveness(boolean)} also needs to be {@code true} for
* mixed MIME type selections to be made.
*
* @param allowVideoMixedMimeTypeAdaptiveness Whether to allow adaptive video selections
* containing mixed MIME types.
* @return This builder.
*/
public ParametersBuilder setAllowVideoMixedMimeTypeAdaptiveness(
@ -311,8 +327,11 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/**
* See {@link Parameters#allowVideoNonSeamlessAdaptiveness}.
* Sets whether to allow adaptive video selections where adaptation may not be completely
* seamless.
*
* @param allowVideoNonSeamlessAdaptiveness Whether to allow adaptive video selections where
* adaptation may not be completely seamless.
* @return This builder.
*/
public ParametersBuilder setAllowVideoNonSeamlessAdaptiveness(
@ -326,7 +345,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* obtained from {@link Util#getPhysicalDisplaySize(Context)}.
*
* @param context Any context.
* @param viewportOrientationMayChange See {@link Parameters#viewportOrientationMayChange}.
* @param viewportOrientationMayChange Whether the viewport orientation may change during
* playback.
* @return This builder.
*/
public ParametersBuilder setViewportSizeToPhysicalDisplaySize(
@ -347,12 +367,13 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/**
* See {@link Parameters#viewportWidth}, {@link Parameters#maxVideoHeight} and {@link
* Parameters#viewportOrientationMayChange}.
* Sets the viewport size to constrain adaptive video selections so that only tracks suitable
* for the viewport are selected.
*
* @param viewportWidth See {@link Parameters#viewportWidth}.
* @param viewportHeight See {@link Parameters#viewportHeight}.
* @param viewportOrientationMayChange See {@link Parameters#viewportOrientationMayChange}.
* @param viewportWidth Viewport width in pixels.
* @param viewportHeight Viewport height in pixels.
* @param viewportOrientationMayChange Whether the viewport orientation may change during
* playback.
* @return This builder.
*/
public ParametersBuilder setViewportSize(
@ -372,8 +393,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/**
* See {@link Parameters#maxAudioChannelCount}.
* Sets the maximum allowed audio channel count.
*
* @param maxAudioChannelCount Maximum allowed audio channel count.
* @return This builder.
*/
public ParametersBuilder setMaxAudioChannelCount(int maxAudioChannelCount) {
@ -382,8 +404,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/**
* See {@link Parameters#maxAudioBitrate}.
* Sets the maximum allowed audio bitrate.
*
* @param maxAudioBitrate Maximum allowed audio bitrate in bits per second.
* @return This builder.
*/
public ParametersBuilder setMaxAudioBitrate(int maxAudioBitrate) {
@ -392,8 +415,11 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/**
* See {@link Parameters#exceedAudioConstraintsIfNecessary}.
* Sets whether to exceed the {@link #setMaxAudioChannelCount(int)} and {@link
* #setMaxAudioBitrate(int)} constraints when no selection can be made otherwise.
*
* @param exceedAudioConstraintsIfNecessary Whether to exceed audio constraints when no
* selection can be made otherwise.
* @return This builder.
*/
public ParametersBuilder setExceedAudioConstraintsIfNecessary(
@ -403,8 +429,12 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/**
* See {@link Parameters#allowAudioMixedMimeTypeAdaptiveness}.
* Sets whether to allow adaptive audio selections containing mixed MIME types.
*
* <p>Adaptations between different MIME types may not be completely seamless.
*
* @param allowAudioMixedMimeTypeAdaptiveness Whether to allow adaptive audio selections
* containing mixed MIME types.
* @return This builder.
*/
public ParametersBuilder setAllowAudioMixedMimeTypeAdaptiveness(
@ -414,8 +444,12 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/**
* See {@link Parameters#allowAudioMixedSampleRateAdaptiveness}.
* Sets whether to allow adaptive audio selections containing mixed sample rates.
*
* <p>Adaptations between different sample rates may not be completely seamless.
*
* @param allowAudioMixedSampleRateAdaptiveness Whether to allow adaptive audio selections
* containing mixed sample rates.
* @return This builder.
*/
public ParametersBuilder setAllowAudioMixedSampleRateAdaptiveness(
@ -424,6 +458,21 @@ public class DefaultTrackSelector extends MappingTrackSelector {
return this;
}
/**
* Sets whether to allow adaptive audio selections containing mixed channel counts.
*
* <p>Adaptations between different channel counts may not be completely seamless.
*
* @param allowAudioMixedChannelCountAdaptiveness Whether to allow adaptive audio selections
* containing mixed channel counts.
* @return This builder.
*/
public ParametersBuilder setAllowAudioMixedChannelCountAdaptiveness(
boolean allowAudioMixedChannelCountAdaptiveness) {
this.allowAudioMixedChannelCountAdaptiveness = allowAudioMixedChannelCountAdaptiveness;
return this;
}
// Text
@Override
@ -454,8 +503,11 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// General
/**
* See {@link Parameters#forceLowestBitrate}.
* Sets whether to force selection of the single lowest bitrate audio and video tracks that
* comply with all other constraints.
*
* @param forceLowestBitrate Whether to force selection of the single lowest bitrate audio and
* video tracks.
* @return This builder.
*/
public ParametersBuilder setForceLowestBitrate(boolean forceLowestBitrate) {
@ -464,8 +516,11 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/**
* See {@link Parameters#forceHighestSupportedBitrate}.
* Sets whether to force selection of the highest bitrate audio and video tracks that comply
* with all other constraints.
*
* @param forceHighestSupportedBitrate Whether to force selection of the highest bitrate audio
* and video tracks.
* @return This builder.
*/
public ParametersBuilder setForceHighestSupportedBitrate(boolean forceHighestSupportedBitrate) {
@ -491,8 +546,15 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/**
* See {@link Parameters#exceedRendererCapabilitiesIfNecessary}.
* Sets whether to exceed renderer capabilities when no selection can be made otherwise.
*
* <p>This parameter applies when all of the tracks available for a renderer exceed the
* renderer's reported capabilities. If the parameter is {@code true} then the lowest quality
* track will still be selected. Playback may succeed if the renderer has under-reported its
* true capabilities. If {@code false} then no track will be selected.
*
* @param exceedRendererCapabilitiesIfNecessary Whether to exceed renderer capabilities when no
* selection can be made otherwise.
* @return This builder.
*/
public ParametersBuilder setExceedRendererCapabilitiesIfNecessary(
@ -502,7 +564,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/**
* See {@link Parameters#tunnelingAudioSessionId}.
* Sets the audio session id to use when tunneling.
*
* <p>Enables or disables tunneling. To enable tunneling, pass an audio session id to use when
* in tunneling mode. Session ids can be generated using {@link
@ -512,6 +574,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
*
* @param tunnelingAudioSessionId The audio session id to use when tunneling, or {@link
* C#AUDIO_SESSION_ID_UNSET} to disable tunneling.
* @return This builder.
*/
public ParametersBuilder setTunnelingAudioSessionId(int tunnelingAudioSessionId) {
this.tunnelingAudioSessionId = tunnelingAudioSessionId;
@ -526,6 +589,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
*
* @param rendererIndex The renderer index.
* @param disabled Whether the renderer is disabled.
* @return This builder.
*/
public final ParametersBuilder setRendererDisabled(int rendererIndex, boolean disabled) {
if (rendererDisabledFlags.get(rendererIndex) == disabled) {
@ -562,6 +626,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* @param rendererIndex The renderer index.
* @param groups The {@link TrackGroupArray} for which the override should be applied.
* @param override The override.
* @return This builder.
*/
public final ParametersBuilder setSelectionOverride(
int rendererIndex, TrackGroupArray groups, SelectionOverride override) {
@ -583,6 +648,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
*
* @param rendererIndex The renderer index.
* @param groups The {@link TrackGroupArray} for which the override should be cleared.
* @return This builder.
*/
public final ParametersBuilder clearSelectionOverride(
int rendererIndex, TrackGroupArray groups) {
@ -602,6 +668,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* Clears all track selection overrides for the specified renderer.
*
* @param rendererIndex The renderer index.
* @return This builder.
*/
public final ParametersBuilder clearSelectionOverrides(int rendererIndex) {
Map<TrackGroupArray, SelectionOverride> overrides = selectionOverrides.get(rendererIndex);
@ -613,7 +680,11 @@ public class DefaultTrackSelector extends MappingTrackSelector {
return this;
}
/** Clears all track selection overrides for all renderers. */
/**
* Clears all track selection overrides for all renderers.
*
* @return This builder.
*/
public final ParametersBuilder clearSelectionOverrides() {
if (selectionOverrides.size() == 0) {
// Nothing to clear.
@ -646,6 +717,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
exceedAudioConstraintsIfNecessary,
allowAudioMixedMimeTypeAdaptiveness,
allowAudioMixedSampleRateAdaptiveness,
allowAudioMixedChannelCountAdaptiveness,
// Text
preferredTextLanguage,
preferredRoleFlags,
@ -695,8 +767,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// Video
/**
* Maximum allowed video width. The default value is {@link Integer#MAX_VALUE} (i.e. no
* constraint).
* Maximum allowed video width in pixels. The default value is {@link Integer#MAX_VALUE} (i.e.
* no constraint).
*
* <p>To constrain adaptive video track selections to be suitable for a given viewport (the
* region of the display within which video will be played), use ({@link #viewportWidth}, {@link
@ -704,8 +776,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
*/
public final int maxVideoWidth;
/**
* Maximum allowed video height. The default value is {@link Integer#MAX_VALUE} (i.e. no
* constraint).
* Maximum allowed video height in pixels. The default value is {@link Integer#MAX_VALUE} (i.e.
* no constraint).
*
* <p>To constrain adaptive video track selections to be suitable for a given viewport (the
* region of the display within which video will be played), use ({@link #viewportWidth}, {@link
@ -713,12 +785,13 @@ public class DefaultTrackSelector extends MappingTrackSelector {
*/
public final int maxVideoHeight;
/**
* Maximum allowed video frame rate. The default value is {@link Integer#MAX_VALUE} (i.e. no
* constraint).
* Maximum allowed video frame rate in hertz. The default value is {@link Integer#MAX_VALUE}
* (i.e. no constraint).
*/
public final int maxVideoFrameRate;
/**
* Maximum video bitrate. The default value is {@link Integer#MAX_VALUE} (i.e. no constraint).
* Maximum allowed video bitrate in bits per second. The default value is {@link
* Integer#MAX_VALUE} (i.e. no constraint).
*/
public final int maxVideoBitrate;
/**
@ -728,9 +801,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
*/
public final boolean exceedVideoConstraintsIfNecessary;
/**
* Whether to allow adaptive video selections containing mixed mime types. Adaptations between
* different mime types may not be completely seamless, in which case {@link
* #allowVideoNonSeamlessAdaptiveness} also needs to be {@code true} for mixed mime type
* Whether to allow adaptive video selections containing mixed MIME types. Adaptations between
* different MIME types may not be completely seamless, in which case {@link
* #allowVideoNonSeamlessAdaptiveness} also needs to be {@code true} for mixed MIME type
* selections to be made. The default value is {@code false}.
*/
public final boolean allowVideoMixedMimeTypeAdaptiveness;
@ -764,7 +837,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
*/
public final int maxAudioChannelCount;
/**
* Maximum audio bitrate. The default value is {@link Integer#MAX_VALUE} (i.e. no constraint).
* Maximum allowed audio bitrate in bits per second. The default value is {@link
* Integer#MAX_VALUE} (i.e. no constraint).
*/
public final int maxAudioBitrate;
/**
@ -773,8 +847,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
*/
public final boolean exceedAudioConstraintsIfNecessary;
/**
* Whether to allow adaptive audio selections containing mixed mime types. Adaptations between
* different mime types may not be completely seamless. The default value is {@code false}.
* Whether to allow adaptive audio selections containing mixed MIME types. Adaptations between
* different MIME types may not be completely seamless. The default value is {@code false}.
*/
public final boolean allowAudioMixedMimeTypeAdaptiveness;
/**
@ -782,6 +856,12 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* different sample rates may not be completely seamless. The default value is {@code false}.
*/
public final boolean allowAudioMixedSampleRateAdaptiveness;
/**
* Whether to allow adaptive audio selections containing mixed channel counts. Adaptations
* between different channel counts may not be completely seamless. The default value is {@code
* false}.
*/
public final boolean allowAudioMixedChannelCountAdaptiveness;
// General
/**
@ -842,6 +922,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
/* exceedAudioConstraintsIfNecessary= */ true,
/* allowAudioMixedMimeTypeAdaptiveness= */ false,
/* allowAudioMixedSampleRateAdaptiveness= */ false,
/* allowAudioMixedChannelCountAdaptiveness= */ false,
// Text
TrackSelectionParameters.DEFAULT.preferredTextLanguage,
TrackSelectionParameters.DEFAULT.preferredRoleFlags,
@ -875,6 +956,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
boolean exceedAudioConstraintsIfNecessary,
boolean allowAudioMixedMimeTypeAdaptiveness,
boolean allowAudioMixedSampleRateAdaptiveness,
boolean allowAudioMixedChannelCountAdaptiveness,
// Text
@Nullable String preferredTextLanguage,
@C.RoleFlags int preferredRoleFlags,
@ -911,6 +993,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
this.exceedAudioConstraintsIfNecessary = exceedAudioConstraintsIfNecessary;
this.allowAudioMixedMimeTypeAdaptiveness = allowAudioMixedMimeTypeAdaptiveness;
this.allowAudioMixedSampleRateAdaptiveness = allowAudioMixedSampleRateAdaptiveness;
this.allowAudioMixedChannelCountAdaptiveness = allowAudioMixedChannelCountAdaptiveness;
// General
this.forceLowestBitrate = forceLowestBitrate;
this.forceHighestSupportedBitrate = forceHighestSupportedBitrate;
@ -944,6 +1027,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
this.exceedAudioConstraintsIfNecessary = Util.readBoolean(in);
this.allowAudioMixedMimeTypeAdaptiveness = Util.readBoolean(in);
this.allowAudioMixedSampleRateAdaptiveness = Util.readBoolean(in);
this.allowAudioMixedChannelCountAdaptiveness = Util.readBoolean(in);
// General
this.forceLowestBitrate = Util.readBoolean(in);
this.forceHighestSupportedBitrate = Util.readBoolean(in);
@ -1025,6 +1109,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
&& exceedAudioConstraintsIfNecessary == other.exceedAudioConstraintsIfNecessary
&& allowAudioMixedMimeTypeAdaptiveness == other.allowAudioMixedMimeTypeAdaptiveness
&& allowAudioMixedSampleRateAdaptiveness == other.allowAudioMixedSampleRateAdaptiveness
&& allowAudioMixedChannelCountAdaptiveness
== other.allowAudioMixedChannelCountAdaptiveness
// General
&& forceLowestBitrate == other.forceLowestBitrate
&& forceHighestSupportedBitrate == other.forceHighestSupportedBitrate
@ -1055,6 +1141,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
result = 31 * result + (exceedAudioConstraintsIfNecessary ? 1 : 0);
result = 31 * result + (allowAudioMixedMimeTypeAdaptiveness ? 1 : 0);
result = 31 * result + (allowAudioMixedSampleRateAdaptiveness ? 1 : 0);
result = 31 * result + (allowAudioMixedChannelCountAdaptiveness ? 1 : 0);
// General
result = 31 * result + (forceLowestBitrate ? 1 : 0);
result = 31 * result + (forceHighestSupportedBitrate ? 1 : 0);
@ -1091,6 +1178,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
Util.writeBoolean(dest, exceedAudioConstraintsIfNecessary);
Util.writeBoolean(dest, allowAudioMixedMimeTypeAdaptiveness);
Util.writeBoolean(dest, allowAudioMixedSampleRateAdaptiveness);
Util.writeBoolean(dest, allowAudioMixedChannelCountAdaptiveness);
// General
Util.writeBoolean(dest, forceLowestBitrate);
Util.writeBoolean(dest, forceHighestSupportedBitrate);
@ -1999,7 +2087,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
formatSupports[selectedGroupIndex],
params.maxAudioBitrate,
params.allowAudioMixedMimeTypeAdaptiveness,
params.allowAudioMixedSampleRateAdaptiveness);
params.allowAudioMixedSampleRateAdaptiveness,
params.allowAudioMixedChannelCountAdaptiveness);
if (adaptiveTracks.length > 0) {
definition = new TrackSelection.Definition(selectedGroup, adaptiveTracks);
}
@ -2017,7 +2106,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
int[] formatSupport,
int maxAudioBitrate,
boolean allowMixedMimeTypeAdaptiveness,
boolean allowMixedSampleRateAdaptiveness) {
boolean allowMixedSampleRateAdaptiveness,
boolean allowAudioMixedChannelCountAdaptiveness) {
int selectedConfigurationTrackCount = 0;
AudioConfigurationTuple selectedConfiguration = null;
HashSet<AudioConfigurationTuple> seenConfigurationTuples = new HashSet<>();
@ -2034,7 +2124,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
configuration,
maxAudioBitrate,
allowMixedMimeTypeAdaptiveness,
allowMixedSampleRateAdaptiveness);
allowMixedSampleRateAdaptiveness,
allowAudioMixedChannelCountAdaptiveness);
if (configurationCount > selectedConfigurationTrackCount) {
selectedConfiguration = configuration;
selectedConfigurationTrackCount = configurationCount;
@ -2054,7 +2145,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
selectedConfiguration,
maxAudioBitrate,
allowMixedMimeTypeAdaptiveness,
allowMixedSampleRateAdaptiveness)) {
allowMixedSampleRateAdaptiveness,
allowAudioMixedChannelCountAdaptiveness)) {
adaptiveIndices[index++] = i;
}
}
@ -2069,7 +2161,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
AudioConfigurationTuple configuration,
int maxAudioBitrate,
boolean allowMixedMimeTypeAdaptiveness,
boolean allowMixedSampleRateAdaptiveness) {
boolean allowMixedSampleRateAdaptiveness,
boolean allowAudioMixedChannelCountAdaptiveness) {
int count = 0;
for (int i = 0; i < group.length; i++) {
if (isSupportedAdaptiveAudioTrack(
@ -2078,7 +2171,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
configuration,
maxAudioBitrate,
allowMixedMimeTypeAdaptiveness,
allowMixedSampleRateAdaptiveness)) {
allowMixedSampleRateAdaptiveness,
allowAudioMixedChannelCountAdaptiveness)) {
count++;
}
}
@ -2091,11 +2185,13 @@ public class DefaultTrackSelector extends MappingTrackSelector {
AudioConfigurationTuple configuration,
int maxAudioBitrate,
boolean allowMixedMimeTypeAdaptiveness,
boolean allowMixedSampleRateAdaptiveness) {
boolean allowMixedSampleRateAdaptiveness,
boolean allowAudioMixedChannelCountAdaptiveness) {
return isSupported(formatSupport, false)
&& (format.bitrate == Format.NO_VALUE || format.bitrate <= maxAudioBitrate)
&& (format.channelCount != Format.NO_VALUE
&& format.channelCount == configuration.channelCount)
&& (allowAudioMixedChannelCountAdaptiveness
|| (format.channelCount != Format.NO_VALUE
&& format.channelCount == configuration.channelCount))
&& (allowMixedMimeTypeAdaptiveness
|| (format.sampleMimeType != null
&& TextUtils.equals(format.sampleMimeType, configuration.mimeType)))

View file

@ -55,9 +55,10 @@ public class TrackSelectionParameters implements Parcelable {
}
/**
* See {@link TrackSelectionParameters#preferredAudioLanguage}.
* Sets the preferred language for audio and forced text tracks.
*
* @param preferredAudioLanguage Preferred audio language as an IETF BCP 47 conformant tag.
* @param preferredAudioLanguage Preferred audio language as an IETF BCP 47 conformant tag, or
* {@code null} to select the default track, or the first track if there's no default.
* @return This builder.
*/
public Builder setPreferredAudioLanguage(@Nullable String preferredAudioLanguage) {
@ -66,9 +67,10 @@ public class TrackSelectionParameters implements Parcelable {
}
/**
* See {@link TrackSelectionParameters#preferredTextLanguage}.
* Sets the preferred language for text tracks.
*
* @param preferredTextLanguage Preferred text language as an IETF BCP 47 conformant tag.
* @param preferredTextLanguage Preferred text language as an IETF BCP 47 conformant tag, or
* {@code null} to select the default track if there is one, or no track otherwise.
* @return This builder.
*/
public Builder setPreferredTextLanguage(@Nullable String preferredTextLanguage) {
@ -88,8 +90,12 @@ public class TrackSelectionParameters implements Parcelable {
}
/**
* See {@link TrackSelectionParameters#selectUndeterminedTextLanguage}.
* Sets whether a text track with undetermined language should be selected if no track with
* {@link #setPreferredTextLanguage(String)} is available, or if the preferred language is
* unset.
*
* @param selectUndeterminedTextLanguage Whether a text track with undetermined language should
* be selected if no preferred language track is available.
* @return This builder.
*/
public Builder setSelectUndeterminedTextLanguage(boolean selectUndeterminedTextLanguage) {
@ -98,8 +104,10 @@ public class TrackSelectionParameters implements Parcelable {
}
/**
* See {@link TrackSelectionParameters#disabledTextTrackSelectionFlags}.
* Sets a bitmask of selection flags that are disabled for text track selections.
*
* @param disabledTextTrackSelectionFlags A bitmask of {@link C.SelectionFlags} that are
* disabled for text track selections.
* @return This builder.
*/
public Builder setDisabledTextTrackSelectionFlags(

View file

@ -15,6 +15,8 @@
*/
package com.google.android.exoplayer2.metadata.emsg;
import static com.google.android.exoplayer2.testutil.TestUtil.createByteArray;
import static com.google.android.exoplayer2.testutil.TestUtil.joinByteArrays;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@ -30,18 +32,19 @@ public final class EventMessageDecoderTest {
@Test
public void testDecodeEventMessage() {
byte[] rawEmsgBody = new byte[] {
117, 114, 110, 58, 116, 101, 115, 116, 0, // scheme_id_uri = "urn:test"
49, 50, 51, 0, // value = "123"
0, 0, -69, -128, // timescale = 48000
0, 0, -69, -128, // presentation_time_delta = 48000
0, 2, 50, -128, // event_duration = 144000
0, 15, 67, -45, // id = 1000403
0, 1, 2, 3, 4}; // message_data = {0, 1, 2, 3, 4}
byte[] rawEmsgBody =
joinByteArrays(
createByteArray(117, 114, 110, 58, 116, 101, 115, 116, 0), // scheme_id_uri = "urn:test"
createByteArray(49, 50, 51, 0), // value = "123"
createByteArray(0, 0, 11, 184), // event_duration_ms = 3000
createByteArray(0, 15, 67, 211), // id = 1000403
createByteArray(0, 1, 2, 3, 4)); // message_data = {0, 1, 2, 3, 4}
EventMessageDecoder decoder = new EventMessageDecoder();
MetadataInputBuffer buffer = new MetadataInputBuffer();
buffer.data = ByteBuffer.allocate(rawEmsgBody.length).put(rawEmsgBody);
Metadata metadata = decoder.decode(buffer);
assertThat(metadata.length()).isEqualTo(1);
EventMessage eventMessage = (EventMessage) metadata.get(0);
assertThat(eventMessage.schemeIdUri).isEqualTo("urn:test");

View file

@ -15,6 +15,8 @@
*/
package com.google.android.exoplayer2.metadata.emsg;
import static com.google.android.exoplayer2.testutil.TestUtil.createByteArray;
import static com.google.android.exoplayer2.testutil.TestUtil.joinByteArrays;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@ -29,67 +31,52 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public final class EventMessageEncoderTest {
private static final EventMessage DECODED_MESSAGE =
new EventMessage("urn:test", "123", 3000, 1000403, new byte[] {0, 1, 2, 3, 4});
private static final byte[] ENCODED_MESSAGE =
joinByteArrays(
createByteArray(117, 114, 110, 58, 116, 101, 115, 116, 0), // scheme_id_uri = "urn:test"
createByteArray(49, 50, 51, 0), // value = "123"
createByteArray(0, 0, 11, 184), // event_duration_ms = 3000
createByteArray(0, 15, 67, 211), // id = 1000403
createByteArray(0, 1, 2, 3, 4)); // message_data = {0, 1, 2, 3, 4}
@Test
public void testEncodeEventStream() throws IOException {
EventMessage eventMessage =
new EventMessage("urn:test", "123", 3000, 1000403, new byte[] {0, 1, 2, 3, 4});
byte[] expectedEmsgBody =
new byte[] {
117, 114, 110, 58, 116, 101, 115, 116, 0, // scheme_id_uri = "urn:test"
49, 50, 51, 0, // value = "123"
0, 0, 3, -24, // timescale = 1000
0, 0, 0, 0, // presentation_time_delta = 0
0, 0, 11, -72, // event_duration = 3000
0, 15, 67, -45, // id = 1000403
0, 1, 2, 3, 4
}; // message_data = {0, 1, 2, 3, 4}
byte[] encodedByteArray = new EventMessageEncoder().encode(eventMessage);
assertThat(encodedByteArray).isEqualTo(expectedEmsgBody);
byte[] foo = new byte[] {1, 2, 3};
byte[] encodedByteArray = new EventMessageEncoder().encode(DECODED_MESSAGE);
assertThat(encodedByteArray).isEqualTo(ENCODED_MESSAGE);
}
@Test
public void testEncodeDecodeEventStream() throws IOException {
EventMessage expectedEmsg =
new EventMessage("urn:test", "123", 3000, 1000403, new byte[] {0, 1, 2, 3, 4});
byte[] encodedByteArray = new EventMessageEncoder().encode(expectedEmsg);
byte[] encodedByteArray = new EventMessageEncoder().encode(DECODED_MESSAGE);
MetadataInputBuffer buffer = new MetadataInputBuffer();
buffer.data = ByteBuffer.allocate(encodedByteArray.length).put(encodedByteArray);
EventMessageDecoder decoder = new EventMessageDecoder();
Metadata metadata = decoder.decode(buffer);
assertThat(metadata.length()).isEqualTo(1);
assertThat(metadata.get(0)).isEqualTo(expectedEmsg);
assertThat(metadata.get(0)).isEqualTo(DECODED_MESSAGE);
}
@Test
public void testEncodeEventStreamMultipleTimesWorkingCorrectly() throws IOException {
EventMessage eventMessage =
new EventMessage("urn:test", "123", 3000, 1000403, new byte[] {0, 1, 2, 3, 4});
byte[] expectedEmsgBody =
new byte[] {
117, 114, 110, 58, 116, 101, 115, 116, 0, // scheme_id_uri = "urn:test"
49, 50, 51, 0, // value = "123"
0, 0, 3, -24, // timescale = 1000
0, 0, 0, 0, // presentation_time_delta = 0
0, 0, 11, -72, // event_duration = 3000
0, 15, 67, -45, // id = 1000403
0, 1, 2, 3, 4
}; // message_data = {0, 1, 2, 3, 4}
EventMessage eventMessage1 =
new EventMessage("urn:test", "123", 3000, 1000402, new byte[] {4, 3, 2, 1, 0});
byte[] expectedEmsgBody1 =
new byte[] {
117, 114, 110, 58, 116, 101, 115, 116, 0, // scheme_id_uri = "urn:test"
49, 50, 51, 0, // value = "123"
0, 0, 3, -24, // timescale = 1000
0, 0, 0, 0, // presentation_time_delta = 0
0, 0, 11, -72, // event_duration = 3000
0, 15, 67, -46, // id = 1000402
4, 3, 2, 1, 0
}; // message_data = {4, 3, 2, 1, 0}
joinByteArrays(
createByteArray(117, 114, 110, 58, 116, 101, 115, 116, 0), // scheme_id_uri = "urn:test"
createByteArray(49, 50, 51, 0), // value = "123"
createByteArray(0, 0, 11, 184), // event_duration_ms = 3000
createByteArray(0, 15, 67, 210), // id = 1000402
createByteArray(4, 3, 2, 1, 0)); // message_data = {4, 3, 2, 1, 0}
EventMessageEncoder eventMessageEncoder = new EventMessageEncoder();
byte[] encodedByteArray = eventMessageEncoder.encode(eventMessage);
assertThat(encodedByteArray).isEqualTo(expectedEmsgBody);
byte[] encodedByteArray = eventMessageEncoder.encode(DECODED_MESSAGE);
assertThat(encodedByteArray).isEqualTo(ENCODED_MESSAGE);
byte[] encodedByteArray1 = eventMessageEncoder.encode(eventMessage1);
assertThat(encodedByteArray1).isEqualTo(expectedEmsgBody1);
}

View file

@ -148,6 +148,7 @@ public final class DefaultTrackSelectorTest {
/* exceedAudioConstraintsIfNecessary= */ false,
/* allowAudioMixedMimeTypeAdaptiveness= */ true,
/* allowAudioMixedSampleRateAdaptiveness= */ false,
/* allowAudioMixedChannelCountAdaptiveness= */ true,
// Text
/* preferredTextLanguage= */ "de",
/* selectUndeterminedTextLanguage= */ true,

View file

@ -41,8 +41,10 @@ android {
dependencies {
implementation project(modulePrefix + 'library-core')
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkVersion
implementation 'androidx.annotation:annotation:1.1.0'
testImplementation project(modulePrefix + 'testutils-robolectric')
testImplementation project(modulePrefix + 'testutils')
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
}
ext {

View file

@ -60,6 +60,7 @@ import java.util.IdentityHashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/** A DASH {@link MediaPeriod}. */
/* package */ final class DashMediaPeriod
@ -245,8 +246,12 @@ import java.util.regex.Pattern;
}
@Override
public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags,
SampleStream[] streams, boolean[] streamResetFlags, long positionUs) {
public long selectTracks(
@NullableType TrackSelection[] selections,
boolean[] mayRetainStreamFlags,
@NullableType SampleStream[] streams,
boolean[] streamResetFlags,
long positionUs) {
int[] streamIndexToTrackGroupIndex = getStreamIndexToTrackGroupIndex(selections);
releaseDisabledStreams(selections, mayRetainStreamFlags, streams);
releaseOrphanEmbeddedStreams(selections, streams, streamIndexToTrackGroupIndex);

View file

@ -130,7 +130,7 @@ public final class DashMediaSource extends BaseMediaSource {
* @return This factory, for convenience.
* @throws IllegalStateException If one of the {@code create} methods has already been called.
*/
public Factory setTag(Object tag) {
public Factory setTag(@Nullable Object tag) {
Assertions.checkState(!isCreateCalled);
this.tag = tag;
return this;
@ -430,8 +430,8 @@ public final class DashMediaSource extends BaseMediaSource {
public DashMediaSource(
DashManifest manifest,
DashChunkSource.Factory chunkSourceFactory,
Handler eventHandler,
MediaSourceEventListener eventListener) {
@Nullable Handler eventHandler,
@Nullable MediaSourceEventListener eventListener) {
this(
manifest,
chunkSourceFactory,
@ -455,8 +455,8 @@ public final class DashMediaSource extends BaseMediaSource {
DashManifest manifest,
DashChunkSource.Factory chunkSourceFactory,
int minLoadableRetryCount,
Handler eventHandler,
MediaSourceEventListener eventListener) {
@Nullable Handler eventHandler,
@Nullable MediaSourceEventListener eventListener) {
this(
manifest,
/* manifestUri= */ null,
@ -492,8 +492,8 @@ public final class DashMediaSource extends BaseMediaSource {
Uri manifestUri,
DataSource.Factory manifestDataSourceFactory,
DashChunkSource.Factory chunkSourceFactory,
Handler eventHandler,
MediaSourceEventListener eventListener) {
@Nullable Handler eventHandler,
@Nullable MediaSourceEventListener eventListener) {
this(
manifestUri,
manifestDataSourceFactory,
@ -529,8 +529,8 @@ public final class DashMediaSource extends BaseMediaSource {
DashChunkSource.Factory chunkSourceFactory,
int minLoadableRetryCount,
long livePresentationDelayMs,
Handler eventHandler,
MediaSourceEventListener eventListener) {
@Nullable Handler eventHandler,
@Nullable MediaSourceEventListener eventListener) {
this(
manifestUri,
manifestDataSourceFactory,
@ -569,8 +569,8 @@ public final class DashMediaSource extends BaseMediaSource {
DashChunkSource.Factory chunkSourceFactory,
int minLoadableRetryCount,
long livePresentationDelayMs,
Handler eventHandler,
MediaSourceEventListener eventListener) {
@Nullable Handler eventHandler,
@Nullable MediaSourceEventListener eventListener) {
this(
/* manifest= */ null,
manifestUri,
@ -591,10 +591,10 @@ public final class DashMediaSource extends BaseMediaSource {
}
private DashMediaSource(
DashManifest manifest,
Uri manifestUri,
DataSource.Factory manifestDataSourceFactory,
ParsingLoadable.Parser<? extends DashManifest> manifestParser,
@Nullable DashManifest manifest,
@Nullable Uri manifestUri,
@Nullable DataSource.Factory manifestDataSourceFactory,
@Nullable ParsingLoadable.Parser<? extends DashManifest> manifestParser,
DashChunkSource.Factory chunkSourceFactory,
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
DrmSessionManager<?> drmSessionManager,

View file

@ -66,7 +66,8 @@ public final class DashUtil {
* @throws IOException Thrown when there is an error while loading.
* @throws InterruptedException Thrown if the thread was interrupted.
*/
public static @Nullable DrmInitData loadDrmInitData(DataSource dataSource, Period period)
@Nullable
public static DrmInitData loadDrmInitData(DataSource dataSource, Period period)
throws IOException, InterruptedException {
int primaryTrackType = C.TRACK_TYPE_VIDEO;
Representation representation = getFirstRepresentation(period, primaryTrackType);
@ -95,7 +96,8 @@ public final class DashUtil {
* @throws IOException Thrown when there is an error while loading.
* @throws InterruptedException Thrown if the thread was interrupted.
*/
public static @Nullable Format loadSampleFormat(
@Nullable
public static Format loadSampleFormat(
DataSource dataSource, int trackType, Representation representation)
throws IOException, InterruptedException {
ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, trackType,
@ -116,7 +118,8 @@ public final class DashUtil {
* @throws IOException Thrown when there is an error while loading.
* @throws InterruptedException Thrown if the thread was interrupted.
*/
public static @Nullable ChunkIndex loadChunkIndex(
@Nullable
public static ChunkIndex loadChunkIndex(
DataSource dataSource, int trackType, Representation representation)
throws IOException, InterruptedException {
ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, trackType,
@ -138,7 +141,8 @@ public final class DashUtil {
* @throws IOException Thrown when there is an error while loading.
* @throws InterruptedException Thrown if the thread was interrupted.
*/
private static @Nullable ChunkExtractorWrapper loadInitializationData(
@Nullable
private static ChunkExtractorWrapper loadInitializationData(
DataSource dataSource, int trackType, Representation representation, boolean loadIndex)
throws IOException, InterruptedException {
RangedUri initializationUri = representation.getInitializationUri();
@ -187,7 +191,8 @@ public final class DashUtil {
return new ChunkExtractorWrapper(extractor, trackType, format);
}
private static @Nullable Representation getFirstRepresentation(Period period, int type) {
@Nullable
private static Representation getFirstRepresentation(Period period, int type) {
int index = period.getAdaptationSetIndex(type);
if (index == C.INDEX_UNSET) {
return null;
@ -197,5 +202,4 @@ public final class DashUtil {
}
private DashUtil() {}
}

View file

@ -67,7 +67,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
private final int maxSegmentsPerLoad;
public Factory(DataSource.Factory dataSourceFactory) {
this(dataSourceFactory, 1);
this(dataSourceFactory, /* maxSegmentsPerLoad= */ 1);
}
public Factory(DataSource.Factory dataSourceFactory, int maxSegmentsPerLoad) {
@ -633,7 +633,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
Representation representation,
boolean enableEventMessageTrack,
List<Format> closedCaptionFormats,
TrackOutput playerEmsgTrackOutput) {
@Nullable TrackOutput playerEmsgTrackOutput) {
this(
periodDurationUs,
representation,
@ -787,7 +787,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
Representation representation,
boolean enableEventMessageTrack,
List<Format> closedCaptionFormats,
TrackOutput playerEmsgTrackOutput) {
@Nullable TrackOutput playerEmsgTrackOutput) {
String containerMimeType = representation.format.containerMimeType;
if (mimeTypeIsRawText(containerMimeType)) {
return null;

View file

@ -80,12 +80,10 @@ public class DashManifest implements FilterableManifest<DashManifest> {
* The {@link UtcTimingElement}, or null if not present. Defined in DVB A168:7/2016, Section
* 4.7.2.
*/
public final UtcTimingElement utcTiming;
@Nullable public final UtcTimingElement utcTiming;
/**
* The location of this manifest.
*/
public final Uri location;
/** The location of this manifest, or null if not present. */
@Nullable public final Uri location;
/** The {@link ProgramInformation}, or null if not present. */
@Nullable public final ProgramInformation programInformation;
@ -106,8 +104,8 @@ public class DashManifest implements FilterableManifest<DashManifest> {
long timeShiftBufferDepthMs,
long suggestedPresentationDelayMs,
long publishTimeMs,
UtcTimingElement utcTiming,
Uri location,
@Nullable UtcTimingElement utcTiming,
@Nullable Uri location,
List<Period> periods) {
this(
availabilityStartTimeMs,
@ -134,8 +132,8 @@ public class DashManifest implements FilterableManifest<DashManifest> {
long suggestedPresentationDelayMs,
long publishTimeMs,
@Nullable ProgramInformation programInformation,
UtcTimingElement utcTiming,
Uri location,
@Nullable UtcTimingElement utcTiming,
@Nullable Uri location,
List<Period> periods) {
this.availabilityStartTimeMs = availabilityStartTimeMs;
this.durationMs = durationMs;

View file

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.source.dash.manifest;
import android.net.Uri;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Pair;
@ -47,6 +48,7 @@ import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.checkerframework.checker.nullness.compatqual.NullableType;
import org.xml.sax.helpers.DefaultHandler;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@ -189,9 +191,9 @@ public class DashManifestParser extends DefaultHandler
long timeShiftBufferDepthMs,
long suggestedPresentationDelayMs,
long publishTimeMs,
ProgramInformation programInformation,
UtcTimingElement utcTiming,
Uri location,
@Nullable ProgramInformation programInformation,
@Nullable UtcTimingElement utcTiming,
@Nullable Uri location,
List<Period> periods) {
return new DashManifest(
availabilityStartTime,
@ -259,8 +261,9 @@ public class DashManifestParser extends DefaultHandler
// AdaptationSet parsing.
protected AdaptationSet parseAdaptationSet(XmlPullParser xpp, String baseUrl,
SegmentBase segmentBase) throws XmlPullParserException, IOException {
protected AdaptationSet parseAdaptationSet(
XmlPullParser xpp, String baseUrl, @Nullable SegmentBase segmentBase)
throws XmlPullParserException, IOException {
int id = parseInt(xpp, "id", AdaptationSet.ID_UNSET);
int contentType = parseContentType(xpp);
@ -394,8 +397,8 @@ public class DashManifestParser extends DefaultHandler
* @return The scheme type and/or {@link SchemeData} parsed from the ContentProtection element.
* Either or both may be null, depending on the ContentProtection element being parsed.
*/
protected Pair<String, SchemeData> parseContentProtection(XmlPullParser xpp)
throws XmlPullParserException, IOException {
protected Pair<@NullableType String, @NullableType SchemeData> parseContentProtection(
XmlPullParser xpp) throws XmlPullParserException, IOException {
String schemeType = null;
String licenseServerUrl = null;
byte[] data = null;
@ -477,19 +480,19 @@ public class DashManifestParser extends DefaultHandler
protected RepresentationInfo parseRepresentation(
XmlPullParser xpp,
String baseUrl,
String label,
String adaptationSetMimeType,
String adaptationSetCodecs,
@Nullable String label,
@Nullable String adaptationSetMimeType,
@Nullable String adaptationSetCodecs,
int adaptationSetWidth,
int adaptationSetHeight,
float adaptationSetFrameRate,
int adaptationSetAudioChannels,
int adaptationSetAudioSamplingRate,
String adaptationSetLanguage,
@Nullable String adaptationSetLanguage,
List<Descriptor> adaptationSetRoleDescriptors,
List<Descriptor> adaptationSetAccessibilityDescriptors,
List<Descriptor> adaptationSetSupplementalProperties,
SegmentBase segmentBase)
@Nullable SegmentBase segmentBase)
throws XmlPullParserException, IOException {
String id = xpp.getAttributeValue(null, "id");
int bandwidth = parseInt(xpp, "bandwidth", Format.NO_VALUE);
@ -564,19 +567,19 @@ public class DashManifestParser extends DefaultHandler
}
protected Format buildFormat(
String id,
String label,
String containerMimeType,
@Nullable String id,
@Nullable String label,
@Nullable String containerMimeType,
int width,
int height,
float frameRate,
int audioChannels,
int audioSamplingRate,
int bitrate,
String language,
@Nullable String language,
List<Descriptor> roleDescriptors,
List<Descriptor> accessibilityDescriptors,
String codecs,
@Nullable String codecs,
List<Descriptor> supplementalProperties) {
String sampleMimeType = getSampleMimeType(containerMimeType, codecs);
@C.SelectionFlags int selectionFlags = parseSelectionFlagsFromRoleDescriptors(roleDescriptors);
@ -650,7 +653,7 @@ public class DashManifestParser extends DefaultHandler
protected Representation buildRepresentation(
RepresentationInfo representationInfo,
String extraDrmSchemeType,
@Nullable String extraDrmSchemeType,
ArrayList<SchemeData> extraDrmSchemeDatas,
ArrayList<Descriptor> extraInbandEventStreams) {
Format format = representationInfo.format;
@ -675,7 +678,8 @@ public class DashManifestParser extends DefaultHandler
// SegmentBase, SegmentList and SegmentTemplate parsing.
protected SingleSegmentBase parseSegmentBase(XmlPullParser xpp, SingleSegmentBase parent)
protected SingleSegmentBase parseSegmentBase(
XmlPullParser xpp, @Nullable SingleSegmentBase parent)
throws XmlPullParserException, IOException {
long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1);
@ -711,7 +715,7 @@ public class DashManifestParser extends DefaultHandler
indexLength);
}
protected SegmentList parseSegmentList(XmlPullParser xpp, SegmentList parent)
protected SegmentList parseSegmentList(XmlPullParser xpp, @Nullable SegmentList parent)
throws XmlPullParserException, IOException {
long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1);
@ -756,15 +760,15 @@ public class DashManifestParser extends DefaultHandler
long presentationTimeOffset,
long startNumber,
long duration,
List<SegmentTimelineElement> timeline,
List<RangedUri> segments) {
@Nullable List<SegmentTimelineElement> timeline,
@Nullable List<RangedUri> segments) {
return new SegmentList(initialization, timescale, presentationTimeOffset,
startNumber, duration, timeline, segments);
}
protected SegmentTemplate parseSegmentTemplate(
XmlPullParser xpp,
SegmentTemplate parent,
@Nullable SegmentTemplate parent,
List<Descriptor> adaptationSetSupplementalProperties)
throws XmlPullParserException, IOException {
long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1);
@ -819,8 +823,8 @@ public class DashManifestParser extends DefaultHandler
long endNumber,
long duration,
List<SegmentTimelineElement> timeline,
UrlTemplate initializationTemplate,
UrlTemplate mediaTemplate) {
@Nullable UrlTemplate initializationTemplate,
@Nullable UrlTemplate mediaTemplate) {
return new SegmentTemplate(
initialization,
timescale,
@ -1008,8 +1012,9 @@ public class DashManifestParser extends DefaultHandler
return new SegmentTimelineElement(elapsedTime, duration);
}
protected UrlTemplate parseUrlTemplate(XmlPullParser xpp, String name,
UrlTemplate defaultValue) {
@Nullable
protected UrlTemplate parseUrlTemplate(
XmlPullParser xpp, String name, @Nullable UrlTemplate defaultValue) {
String valueString = xpp.getAttributeValue(null, name);
if (valueString != null) {
return UrlTemplate.compile(valueString);
@ -1126,7 +1131,7 @@ public class DashManifestParser extends DefaultHandler
}
@C.RoleFlags
protected int parseDashRoleSchemeValue(String value) {
protected int parseDashRoleSchemeValue(@Nullable String value) {
if (value == null) {
return 0;
}
@ -1159,7 +1164,7 @@ public class DashManifestParser extends DefaultHandler
}
@C.RoleFlags
protected int parseTvaAudioPurposeCsValue(String value) {
protected int parseTvaAudioPurposeCsValue(@Nullable String value) {
if (value == null) {
return 0;
}
@ -1230,7 +1235,9 @@ public class DashManifestParser extends DefaultHandler
* @param codecs The codecs attribute.
* @return The derived sample mimeType, or null if it could not be derived.
*/
private static String getSampleMimeType(String containerMimeType, String codecs) {
@Nullable
private static String getSampleMimeType(
@Nullable String containerMimeType, @Nullable String codecs) {
if (MimeTypes.isAudio(containerMimeType)) {
return MimeTypes.getAudioMediaMimeType(codecs);
} else if (MimeTypes.isVideo(containerMimeType)) {
@ -1264,7 +1271,7 @@ public class DashManifestParser extends DefaultHandler
* @param mimeType The mimeType.
* @return Whether the mimeType is a text sample mimeType.
*/
private static boolean mimeTypeIsRawText(String mimeType) {
private static boolean mimeTypeIsRawText(@Nullable String mimeType) {
return MimeTypes.isText(mimeType)
|| MimeTypes.APPLICATION_TTML.equals(mimeType)
|| MimeTypes.APPLICATION_MP4VTT.equals(mimeType)
@ -1273,16 +1280,18 @@ public class DashManifestParser extends DefaultHandler
}
/**
* Checks two languages for consistency, returning the consistent language, or throwing an
* {@link IllegalStateException} if the languages are inconsistent.
* <p>
* Two languages are consistent if they are equal, or if one is null.
* Checks two languages for consistency, returning the consistent language, or throwing an {@link
* IllegalStateException} if the languages are inconsistent.
*
* <p>Two languages are consistent if they are equal, or if one is null.
*
* @param firstLanguage The first language.
* @param secondLanguage The second language.
* @return The consistent language.
*/
private static String checkLanguageConsistency(String firstLanguage, String secondLanguage) {
@Nullable
private static String checkLanguageConsistency(
@Nullable String firstLanguage, @Nullable String secondLanguage) {
if (firstLanguage == null) {
return secondLanguage;
} else if (secondLanguage == null) {
@ -1485,14 +1494,19 @@ public class DashManifestParser extends DefaultHandler
public final Format format;
public final String baseUrl;
public final SegmentBase segmentBase;
public final String drmSchemeType;
@Nullable public final String drmSchemeType;
public final ArrayList<SchemeData> drmSchemeDatas;
public final ArrayList<Descriptor> inbandEventStreams;
public final long revisionId;
public RepresentationInfo(Format format, String baseUrl, SegmentBase segmentBase,
String drmSchemeType, ArrayList<SchemeData> drmSchemeDatas,
ArrayList<Descriptor> inbandEventStreams, long revisionId) {
public RepresentationInfo(
Format format,
String baseUrl,
SegmentBase segmentBase,
@Nullable String drmSchemeType,
ArrayList<SchemeData> drmSchemeDatas,
ArrayList<Descriptor> inbandEventStreams,
long revisionId) {
this.format = format;
this.baseUrl = baseUrl;
this.segmentBase = segmentBase;

View file

@ -15,7 +15,6 @@
*/
package com.google.android.exoplayer2.source.dash.manifest;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.util.Util;
@ -24,10 +23,8 @@ import com.google.android.exoplayer2.util.Util;
*/
public final class Descriptor {
/**
* The scheme URI.
*/
@NonNull public final String schemeIdUri;
/** The scheme URI. */
public final String schemeIdUri;
/**
* The value, or null.
*/
@ -42,7 +39,7 @@ public final class Descriptor {
* @param value The value, or null.
* @param id The identifier, or null.
*/
public Descriptor(@NonNull String schemeIdUri, @Nullable String value, @Nullable String id) {
public Descriptor(String schemeIdUri, @Nullable String value, @Nullable String id) {
this.schemeIdUri = schemeIdUri;
this.value = value;
this.id = id;
@ -63,10 +60,9 @@ public final class Descriptor {
@Override
public int hashCode() {
int result = (schemeIdUri != null ? schemeIdUri.hashCode() : 0);
int result = schemeIdUri.hashCode();
result = 31 * result + (value != null ? value.hashCode() : 0);
result = 31 * result + (id != null ? id.hashCode() : 0);
return result;
}
}

View file

@ -21,22 +21,26 @@ import com.google.android.exoplayer2.util.Util;
/** A parsed program information element. */
public class ProgramInformation {
/** The title for the media presentation. */
public final String title;
@Nullable public final String title;
/** Information about the original source of the media presentation. */
public final String source;
@Nullable public final String source;
/** A copyright statement for the media presentation. */
public final String copyright;
@Nullable public final String copyright;
/** A URL that provides more information about the media presentation. */
public final String moreInformationURL;
@Nullable public final String moreInformationURL;
/** Declares the language code(s) for this ProgramInformation. */
public final String lang;
@Nullable public final String lang;
public ProgramInformation(
String title, String source, String copyright, String moreInformationURL, String lang) {
@Nullable String title,
@Nullable String source,
@Nullable String copyright,
@Nullable String moreInformationURL,
@Nullable String lang) {
this.title = title;
this.source = source;
this.copyright = copyright;

View file

@ -83,7 +83,7 @@ public final class RangedUri {
* <p>If {@code other} is null then the merge is considered unsuccessful, and null is returned.
*
* @param other The {@link RangedUri} to merge.
* @param baseUri The optional base Uri.
* @param baseUri The base Uri.
* @return The merged {@link RangedUri} if the merge was successful. Null otherwise.
*/
@Nullable

View file

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.source.dash.manifest;
import android.net.Uri;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.dash.DashSegmentIndex;
@ -53,9 +54,7 @@ public abstract class Representation {
* The offset of the presentation timestamps in the media stream relative to media time.
*/
public final long presentationTimeOffsetUs;
/**
* The in-band event streams in the representation. Never null, but may be empty.
*/
/** The in-band event streams in the representation. May be empty. */
public final List<Descriptor> inbandEventStreams;
private final RangedUri initializationUri;
@ -71,7 +70,7 @@ public abstract class Representation {
*/
public static Representation newInstance(
long revisionId, Format format, String baseUrl, SegmentBase segmentBase) {
return newInstance(revisionId, format, baseUrl, segmentBase, null);
return newInstance(revisionId, format, baseUrl, segmentBase, /* inbandEventStreams= */ null);
}
/**
@ -89,8 +88,9 @@ public abstract class Representation {
Format format,
String baseUrl,
SegmentBase segmentBase,
List<Descriptor> inbandEventStreams) {
return newInstance(revisionId, format, baseUrl, segmentBase, inbandEventStreams, null);
@Nullable List<Descriptor> inbandEventStreams) {
return newInstance(
revisionId, format, baseUrl, segmentBase, inbandEventStreams, /* cacheKey= */ null);
}
/**
@ -110,8 +110,8 @@ public abstract class Representation {
Format format,
String baseUrl,
SegmentBase segmentBase,
List<Descriptor> inbandEventStreams,
String cacheKey) {
@Nullable List<Descriptor> inbandEventStreams,
@Nullable String cacheKey) {
if (segmentBase instanceof SingleSegmentBase) {
return new SingleSegmentRepresentation(
revisionId,
@ -135,7 +135,7 @@ public abstract class Representation {
Format format,
String baseUrl,
SegmentBase segmentBase,
List<Descriptor> inbandEventStreams) {
@Nullable List<Descriptor> inbandEventStreams) {
this.revisionId = revisionId;
this.format = format;
this.baseUrl = baseUrl;
@ -151,6 +151,7 @@ public abstract class Representation {
* Returns a {@link RangedUri} defining the location of the representation's initialization data,
* or null if no initialization data exists.
*/
@Nullable
public RangedUri getInitializationUri() {
return initializationUri;
}
@ -159,14 +160,15 @@ public abstract class Representation {
* Returns a {@link RangedUri} defining the location of the representation's segment index, or
* null if the representation provides an index directly.
*/
@Nullable
public abstract RangedUri getIndexUri();
/**
* Returns an index if the representation provides one directly, or null otherwise.
*/
/** Returns an index if the representation provides one directly, or null otherwise. */
@Nullable
public abstract DashSegmentIndex getIndex();
/** Returns a cache key for the representation if set, or null. */
@Nullable
public abstract String getCacheKey();
/**
@ -184,9 +186,9 @@ public abstract class Representation {
*/
public final long contentLength;
private final String cacheKey;
private final RangedUri indexUri;
private final SingleSegmentIndex segmentIndex;
@Nullable private final String cacheKey;
@Nullable private final RangedUri indexUri;
@Nullable private final SingleSegmentIndex segmentIndex;
/**
* @param revisionId Identifies the revision of the content.
@ -209,7 +211,7 @@ public abstract class Representation {
long indexStart,
long indexEnd,
List<Descriptor> inbandEventStreams,
String cacheKey,
@Nullable String cacheKey,
long contentLength) {
RangedUri rangedUri = new RangedUri(null, initializationStart,
initializationEnd - initializationStart + 1);
@ -233,8 +235,8 @@ public abstract class Representation {
Format format,
String baseUrl,
SingleSegmentBase segmentBase,
List<Descriptor> inbandEventStreams,
String cacheKey,
@Nullable List<Descriptor> inbandEventStreams,
@Nullable String cacheKey,
long contentLength) {
super(revisionId, format, baseUrl, segmentBase, inbandEventStreams);
this.uri = Uri.parse(baseUrl);
@ -248,16 +250,19 @@ public abstract class Representation {
}
@Override
@Nullable
public RangedUri getIndexUri() {
return indexUri;
}
@Override
@Nullable
public DashSegmentIndex getIndex() {
return segmentIndex;
}
@Override
@Nullable
public String getCacheKey() {
return cacheKey;
}
@ -284,12 +289,13 @@ public abstract class Representation {
Format format,
String baseUrl,
MultiSegmentBase segmentBase,
List<Descriptor> inbandEventStreams) {
@Nullable List<Descriptor> inbandEventStreams) {
super(revisionId, format, baseUrl, segmentBase, inbandEventStreams);
this.segmentBase = segmentBase;
}
@Override
@Nullable
public RangedUri getIndexUri() {
return null;
}
@ -300,6 +306,7 @@ public abstract class Representation {
}
@Override
@Nullable
public String getCacheKey() {
return null;
}

View file

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.source.dash.manifest;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.source.dash.DashSegmentIndex;
import com.google.android.exoplayer2.util.Util;
@ -25,7 +26,7 @@ import java.util.List;
*/
public abstract class SegmentBase {
/* package */ final RangedUri initialization;
/* package */ @Nullable final RangedUri initialization;
/* package */ final long timescale;
/* package */ final long presentationTimeOffset;
@ -36,7 +37,8 @@ public abstract class SegmentBase {
* @param presentationTimeOffset The presentation time offset. The value in seconds is the
* division of this value and {@code timescale}.
*/
public SegmentBase(RangedUri initialization, long timescale, long presentationTimeOffset) {
public SegmentBase(
@Nullable RangedUri initialization, long timescale, long presentationTimeOffset) {
this.initialization = initialization;
this.timescale = timescale;
this.presentationTimeOffset = presentationTimeOffset;
@ -49,6 +51,7 @@ public abstract class SegmentBase {
* @param representation The {@link Representation} for which initialization data is required.
* @return A {@link RangedUri} defining the location of the initialization data, or null.
*/
@Nullable
public RangedUri getInitialization(Representation representation) {
return initialization;
}
@ -77,19 +80,31 @@ public abstract class SegmentBase {
* @param indexStart The byte offset of the index data in the segment.
* @param indexLength The length of the index data in bytes.
*/
public SingleSegmentBase(RangedUri initialization, long timescale, long presentationTimeOffset,
long indexStart, long indexLength) {
public SingleSegmentBase(
@Nullable RangedUri initialization,
long timescale,
long presentationTimeOffset,
long indexStart,
long indexLength) {
super(initialization, timescale, presentationTimeOffset);
this.indexStart = indexStart;
this.indexLength = indexLength;
}
public SingleSegmentBase() {
this(null, 1, 0, 0, 0);
this(
/* initialization= */ null,
/* timescale= */ 1,
/* presentationTimeOffset= */ 0,
/* indexStart= */ 0,
/* indexLength= */ 0);
}
@Nullable
public RangedUri getIndex() {
return indexLength <= 0 ? null : new RangedUri(null, indexStart, indexLength);
return indexLength <= 0
? null
: new RangedUri(/* referenceUri= */ null, indexStart, indexLength);
}
}
@ -101,7 +116,7 @@ public abstract class SegmentBase {
/* package */ final long startNumber;
/* package */ final long duration;
/* package */ final List<SegmentTimelineElement> segmentTimeline;
/* package */ @Nullable final List<SegmentTimelineElement> segmentTimeline;
/**
* @param initialization A {@link RangedUri} corresponding to initialization data, if such data
@ -118,12 +133,12 @@ public abstract class SegmentBase {
* parameter.
*/
public MultiSegmentBase(
RangedUri initialization,
@Nullable RangedUri initialization,
long timescale,
long presentationTimeOffset,
long startNumber,
long duration,
List<SegmentTimelineElement> segmentTimeline) {
@Nullable List<SegmentTimelineElement> segmentTimeline) {
super(initialization, timescale, presentationTimeOffset);
this.startNumber = startNumber;
this.duration = duration;
@ -223,7 +238,7 @@ public abstract class SegmentBase {
*/
public static class SegmentList extends MultiSegmentBase {
/* package */ final List<RangedUri> mediaSegments;
/* package */ @Nullable final List<RangedUri> mediaSegments;
/**
* @param initialization A {@link RangedUri} corresponding to initialization data, if such data
@ -246,8 +261,8 @@ public abstract class SegmentBase {
long presentationTimeOffset,
long startNumber,
long duration,
List<SegmentTimelineElement> segmentTimeline,
List<RangedUri> mediaSegments) {
@Nullable List<SegmentTimelineElement> segmentTimeline,
@Nullable List<RangedUri> mediaSegments) {
super(initialization, timescale, presentationTimeOffset, startNumber, duration,
segmentTimeline);
this.mediaSegments = mediaSegments;
@ -275,8 +290,8 @@ public abstract class SegmentBase {
*/
public static class SegmentTemplate extends MultiSegmentBase {
/* package */ final UrlTemplate initializationTemplate;
/* package */ final UrlTemplate mediaTemplate;
/* package */ @Nullable final UrlTemplate initializationTemplate;
/* package */ @Nullable final UrlTemplate mediaTemplate;
/* package */ final long endNumber;
/**
@ -308,9 +323,9 @@ public abstract class SegmentBase {
long startNumber,
long endNumber,
long duration,
List<SegmentTimelineElement> segmentTimeline,
UrlTemplate initializationTemplate,
UrlTemplate mediaTemplate) {
@Nullable List<SegmentTimelineElement> segmentTimeline,
@Nullable UrlTemplate initializationTemplate,
@Nullable UrlTemplate mediaTemplate) {
super(
initialization,
timescale,
@ -324,6 +339,7 @@ public abstract class SegmentBase {
}
@Override
@Nullable
public RangedUri getInitialization(Representation representation) {
if (initializationTemplate != null) {
String urlString = initializationTemplate.buildUri(representation.format.id, 0,

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.source.dash.manifest;
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.source.dash.offline;
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.source.dash;
import com.google.android.exoplayer2.util.NonNullApi;

View file

@ -41,8 +41,10 @@ android {
dependencies {
implementation 'androidx.annotation:annotation:1.1.0'
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkVersion
implementation project(modulePrefix + 'library-core')
testImplementation project(modulePrefix + 'testutils-robolectric')
testImplementation project(modulePrefix + 'testutils')
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
}
ext {

View file

@ -105,7 +105,8 @@ import javax.crypto.spec.SecretKeySpec;
}
@Override
public final @Nullable Uri getUri() {
@Nullable
public final Uri getUri() {
return upstream.getUri();
}

View file

@ -84,11 +84,11 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
@Override
public Result createExtractor(
Extractor previousExtractor,
@Nullable Extractor previousExtractor,
Uri uri,
Format format,
List<Format> muxedCaptionFormats,
DrmInitData drmInitData,
@Nullable List<Format> muxedCaptionFormats,
@Nullable DrmInitData drmInitData,
TimestampAdjuster timestampAdjuster,
Map<String, List<String>> responseHeaders,
ExtractorInput extractorInput)

View file

@ -59,10 +59,8 @@ import java.util.Map;
clear();
}
/**
* The chunk to be loaded next.
*/
public Chunk chunk;
/** The chunk to be loaded next. */
@Nullable public Chunk chunk;
/**
* Indicates that the end of the stream has been reached.
@ -70,7 +68,7 @@ import java.util.Map;
public boolean endOfStream;
/** Indicates that the chunk source is waiting for the referred playlist to be refreshed. */
public Uri playlistUrl;
@Nullable public Uri playlistUrl;
/**
* Clears the holder.
@ -138,7 +136,7 @@ import java.util.Map;
HlsDataSourceFactory dataSourceFactory,
@Nullable TransferListener mediaTransferListener,
TimestampAdjusterProvider timestampAdjusterProvider,
List<Format> muxedCaptionFormats) {
@Nullable List<Format> muxedCaptionFormats) {
this.extractorFactory = extractorFactory;
this.playlistTracker = playlistTracker;
this.playlistUrls = playlistUrls;

View file

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.source.hls;
import android.net.Uri;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.extractor.Extractor;
@ -82,11 +83,11 @@ public interface HlsExtractorFactory {
* @throws IOException If an I/O error is encountered while sniffing.
*/
Result createExtractor(
Extractor previousExtractor,
@Nullable Extractor previousExtractor,
Uri uri,
Format format,
List<Format> muxedCaptionFormats,
DrmInitData drmInitData,
@Nullable List<Format> muxedCaptionFormats,
@Nullable DrmInitData drmInitData,
TimestampAdjuster timestampAdjuster,
Map<String, List<String>> responseHeaders,
ExtractorInput sniffingExtractorInput)

View file

@ -54,6 +54,7 @@ import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/**
* A {@link MediaPeriod} that loads an HLS stream.
@ -249,8 +250,12 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
}
@Override
public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags,
SampleStream[] streams, boolean[] streamResetFlags, long positionUs) {
public long selectTracks(
@NullableType TrackSelection[] selections,
boolean[] mayRetainStreamFlags,
@NullableType SampleStream[] streams,
boolean[] streamResetFlags,
long positionUs) {
// Map each selection and stream onto a child period index.
int[] streamChildIndices = new int[selections.length];
int[] selectionChildIndices = new int[selections.length];

View file

@ -110,7 +110,7 @@ public final class HlsMediaSource extends BaseMediaSource
* @return This factory, for convenience.
* @throws IllegalStateException If one of the {@code create} methods has already been called.
*/
public Factory setTag(Object tag) {
public Factory setTag(@Nullable Object tag) {
Assertions.checkState(!isCreateCalled);
this.tag = tag;
return this;

View file

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.source.hls;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
@ -49,7 +50,7 @@ public final class WebvttExtractor implements Extractor {
private static final int HEADER_MIN_LENGTH = 6 /* "WEBVTT" */;
private static final int HEADER_MAX_LENGTH = 3 /* optional Byte Order Mark */ + HEADER_MIN_LENGTH;
private final String language;
@Nullable private final String language;
private final TimestampAdjuster timestampAdjuster;
private final ParsableByteArray sampleDataWrapper;
@ -58,7 +59,7 @@ public final class WebvttExtractor implements Extractor {
private byte[] sampleData;
private int sampleSize;
public WebvttExtractor(String language, TimestampAdjuster timestampAdjuster) {
public WebvttExtractor(@Nullable String language, TimestampAdjuster timestampAdjuster) {
this.language = language;
this.timestampAdjuster = timestampAdjuster;
this.sampleDataWrapper = new ParsableByteArray();

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.source.hls.offline;
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.source.hls;
import com.google.android.exoplayer2.util.NonNullApi;

View file

@ -172,6 +172,7 @@ public final class DefaultHlsPlaylistTracker
}
@Override
@Nullable
public HlsMediaPlaylist getPlaylistSnapshot(Uri url, boolean isForPlayback) {
HlsMediaPlaylist snapshot = playlistBundles.get(url).getPlaylistSnapshot();
if (snapshot != null && isForPlayback) {
@ -448,7 +449,7 @@ public final class DefaultHlsPlaylistTracker
private final Loader mediaPlaylistLoader;
private final ParsingLoadable<HlsPlaylist> mediaPlaylistLoadable;
private HlsMediaPlaylist playlistSnapshot;
@Nullable private HlsMediaPlaylist playlistSnapshot;
private long lastSnapshotLoadMs;
private long lastSnapshotChangeMs;
private long earliestNextLoadTimeMs;
@ -467,6 +468,7 @@ public final class DefaultHlsPlaylistTracker
mediaPlaylistParser);
}
@Nullable
public HlsMediaPlaylist getPlaylistSnapshot() {
return playlistSnapshot;
}

View file

@ -174,7 +174,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
* The format of the audio muxed in the variants. May be null if the playlist does not declare any
* muxed audio.
*/
public final Format muxedAudioFormat;
@Nullable public final Format muxedAudioFormat;
/**
* The format of the closed captions declared by the playlist. May be empty if the playlist
* explicitly declares no captions are available, or null if the playlist does not declare any
@ -208,7 +208,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
List<Rendition> audios,
List<Rendition> subtitles,
List<Rendition> closedCaptions,
Format muxedAudioFormat,
@Nullable Format muxedAudioFormat,
List<Format> muxedCaptionFormats,
boolean hasIndependentSegments,
Map<String, String> variableDefinitions,

View file

@ -16,7 +16,6 @@
package com.google.android.exoplayer2.source.hls.playlist;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.drm.DrmInitData;
@ -95,8 +94,8 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
String uri,
long byterangeOffset,
long byterangeLength,
String fullSegmentEncryptionKeyUri,
String encryptionIV) {
@Nullable String fullSegmentEncryptionKeyUri,
@Nullable String encryptionIV) {
this(
uri,
/* initializationSegment= */ null,
@ -154,7 +153,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
}
@Override
public int compareTo(@NonNull Long relativeStartTimeUs) {
public int compareTo(Long relativeStartTimeUs) {
return this.relativeStartTimeUs > relativeStartTimeUs
? 1 : (this.relativeStartTimeUs < relativeStartTimeUs ? -1 : 0);
}

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.source.hls.playlist;
import com.google.android.exoplayer2.util.NonNullApi;

View file

@ -41,8 +41,10 @@ android {
dependencies {
implementation project(modulePrefix + 'library-core')
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkVersion
implementation 'androidx.annotation:annotation:1.1.0'
testImplementation project(modulePrefix + 'testutils-robolectric')
testImplementation project(modulePrefix + 'testutils')
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
}
ext {

View file

@ -38,6 +38,7 @@ import com.google.android.exoplayer2.upstream.TransferListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/** A SmoothStreaming {@link MediaPeriod}. */
/* package */ final class SsMediaPeriod
@ -120,8 +121,12 @@ import java.util.List;
}
@Override
public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags,
SampleStream[] streams, boolean[] streamResetFlags, long positionUs) {
public long selectTracks(
@NullableType TrackSelection[] selections,
boolean[] mayRetainStreamFlags,
@NullableType SampleStream[] streams,
boolean[] streamResetFlags,
long positionUs) {
ArrayList<ChunkSampleStream<SsChunkSource>> sampleStreamsList = new ArrayList<>();
for (int i = 0; i < selections.length; i++) {
if (streams[i] != null) {

View file

@ -115,7 +115,7 @@ public final class SsMediaSource extends BaseMediaSource
* @return This factory, for convenience.
* @throws IllegalStateException If one of the {@code create} methods has already been called.
*/
public Factory setTag(Object tag) {
public Factory setTag(@Nullable Object tag) {
Assertions.checkState(!isCreateCalled);
this.tag = tag;
return this;
@ -370,8 +370,8 @@ public final class SsMediaSource extends BaseMediaSource
public SsMediaSource(
SsManifest manifest,
SsChunkSource.Factory chunkSourceFactory,
Handler eventHandler,
MediaSourceEventListener eventListener) {
@Nullable Handler eventHandler,
@Nullable MediaSourceEventListener eventListener) {
this(
manifest,
chunkSourceFactory,
@ -395,8 +395,8 @@ public final class SsMediaSource extends BaseMediaSource
SsManifest manifest,
SsChunkSource.Factory chunkSourceFactory,
int minLoadableRetryCount,
Handler eventHandler,
MediaSourceEventListener eventListener) {
@Nullable Handler eventHandler,
@Nullable MediaSourceEventListener eventListener) {
this(
manifest,
/* manifestUri= */ null,
@ -431,8 +431,8 @@ public final class SsMediaSource extends BaseMediaSource
Uri manifestUri,
DataSource.Factory manifestDataSourceFactory,
SsChunkSource.Factory chunkSourceFactory,
Handler eventHandler,
MediaSourceEventListener eventListener) {
@Nullable Handler eventHandler,
@Nullable MediaSourceEventListener eventListener) {
this(
manifestUri,
manifestDataSourceFactory,
@ -466,8 +466,8 @@ public final class SsMediaSource extends BaseMediaSource
SsChunkSource.Factory chunkSourceFactory,
int minLoadableRetryCount,
long livePresentationDelayMs,
Handler eventHandler,
MediaSourceEventListener eventListener) {
@Nullable Handler eventHandler,
@Nullable MediaSourceEventListener eventListener) {
this(manifestUri, manifestDataSourceFactory, new SsManifestParser(), chunkSourceFactory,
minLoadableRetryCount, livePresentationDelayMs, eventHandler, eventListener);
}
@ -496,8 +496,8 @@ public final class SsMediaSource extends BaseMediaSource
SsChunkSource.Factory chunkSourceFactory,
int minLoadableRetryCount,
long livePresentationDelayMs,
Handler eventHandler,
MediaSourceEventListener eventListener) {
@Nullable Handler eventHandler,
@Nullable MediaSourceEventListener eventListener) {
this(
/* manifest= */ null,
manifestUri,
@ -515,10 +515,10 @@ public final class SsMediaSource extends BaseMediaSource
}
private SsMediaSource(
SsManifest manifest,
Uri manifestUri,
DataSource.Factory manifestDataSourceFactory,
ParsingLoadable.Parser<? extends SsManifest> manifestParser,
@Nullable SsManifest manifest,
@Nullable Uri manifestUri,
@Nullable DataSource.Factory manifestDataSourceFactory,
@Nullable ParsingLoadable.Parser<? extends SsManifest> manifestParser,
SsChunkSource.Factory chunkSourceFactory,
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
DrmSessionManager<?> drmSessionManager,

View file

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.source.smoothstreaming.manifest;
import android.net.Uri;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.mp4.TrackEncryptionBox;
@ -69,7 +70,7 @@ public class SsManifest implements FilterableManifest<SsManifest> {
public final int maxHeight;
public final int displayWidth;
public final int displayHeight;
public final String language;
@Nullable public final String language;
public final Format[] formats;
public final int chunkCount;
@ -80,9 +81,20 @@ public class SsManifest implements FilterableManifest<SsManifest> {
private final long[] chunkStartTimesUs;
private final long lastChunkDurationUs;
public StreamElement(String baseUri, String chunkTemplate, int type, String subType,
long timescale, String name, int maxWidth, int maxHeight, int displayWidth,
int displayHeight, String language, Format[] formats, List<Long> chunkStartTimes,
public StreamElement(
String baseUri,
String chunkTemplate,
int type,
String subType,
long timescale,
String name,
int maxWidth,
int maxHeight,
int displayWidth,
int displayHeight,
@Nullable String language,
Format[] formats,
List<Long> chunkStartTimes,
long lastChunkDuration) {
this(
baseUri,
@ -102,10 +114,22 @@ public class SsManifest implements FilterableManifest<SsManifest> {
Util.scaleLargeTimestamp(lastChunkDuration, C.MICROS_PER_SECOND, timescale));
}
private StreamElement(String baseUri, String chunkTemplate, int type, String subType,
long timescale, String name, int maxWidth, int maxHeight, int displayWidth,
int displayHeight, String language, Format[] formats, List<Long> chunkStartTimes,
long[] chunkStartTimesUs, long lastChunkDurationUs) {
private StreamElement(
String baseUri,
String chunkTemplate,
int type,
String subType,
long timescale,
String name,
int maxWidth,
int maxHeight,
int displayWidth,
int displayHeight,
@Nullable String language,
Format[] formats,
List<Long> chunkStartTimes,
long[] chunkStartTimesUs,
long lastChunkDurationUs) {
this.baseUri = baseUri;
this.chunkTemplate = chunkTemplate;
this.type = type;
@ -208,7 +232,7 @@ public class SsManifest implements FilterableManifest<SsManifest> {
public final boolean isLive;
/** Content protection information, or null if the content is not protected. */
public final ProtectionElement protectionElement;
@Nullable public final ProtectionElement protectionElement;
/** The contained stream elements. */
public final StreamElement[] streamElements;
@ -249,7 +273,7 @@ public class SsManifest implements FilterableManifest<SsManifest> {
long dvrWindowLength,
int lookAheadCount,
boolean isLive,
ProtectionElement protectionElement,
@Nullable ProtectionElement protectionElement,
StreamElement[] streamElements) {
this(
majorVersion,
@ -273,7 +297,7 @@ public class SsManifest implements FilterableManifest<SsManifest> {
long dvrWindowLengthUs,
int lookAheadCount,
boolean isLive,
ProtectionElement protectionElement,
@Nullable ProtectionElement protectionElement,
StreamElement[] streamElements) {
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;

View file

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.source.smoothstreaming.manifest;
import android.net.Uri;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Pair;
@ -40,6 +41,7 @@ import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import org.checkerframework.checker.nullness.compatqual.NullableType;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
@ -94,10 +96,10 @@ public class SsManifestParser implements ParsingLoadable.Parser<SsManifest> {
private final String baseUri;
private final String tag;
private final ElementParser parent;
private final List<Pair<String, Object>> normalizedAttributes;
@Nullable private final ElementParser parent;
private final List<Pair<String, @NullableType Object>> normalizedAttributes;
public ElementParser(ElementParser parent, String baseUri, String tag) {
public ElementParser(@Nullable ElementParser parent, String baseUri, String tag) {
this.parent = parent;
this.baseUri = baseUri;
this.tag = tag;
@ -174,24 +176,25 @@ public class SsManifestParser implements ParsingLoadable.Parser<SsManifest> {
* Stash an attribute that may be normalized at this level. In other words, an attribute that
* may have been pulled up from the child elements because its value was the same in all
* children.
* <p>
* Stashing an attribute allows child element parsers to retrieve the values of normalized
*
* <p>Stashing an attribute allows child element parsers to retrieve the values of normalized
* attributes using {@link #getNormalizedAttribute(String)}.
*
* @param key The name of the attribute.
* @param value The value of the attribute.
*/
protected final void putNormalizedAttribute(String key, Object value) {
protected final void putNormalizedAttribute(String key, @Nullable Object value) {
normalizedAttributes.add(Pair.create(key, value));
}
/**
* Attempt to retrieve a stashed normalized attribute. If there is no stashed attribute with
* the provided name, the parent element parser will be queried, and so on up the chain.
* Attempt to retrieve a stashed normalized attribute. If there is no stashed attribute with the
* provided name, the parent element parser will be queried, and so on up the chain.
*
* @param key The name of the attribute.
* @return The stashed value, or null if the attribute was not be found.
*/
@Nullable
protected final Object getNormalizedAttribute(String key) {
for (int i = 0; i < normalizedAttributes.size(); i++) {
Pair<String, Object> pair = normalizedAttributes.get(i);
@ -340,7 +343,7 @@ public class SsManifestParser implements ParsingLoadable.Parser<SsManifest> {
private long dvrWindowLength;
private int lookAheadCount;
private boolean isLive;
private ProtectionElement protectionElement;
@Nullable private ProtectionElement protectionElement;
public SmoothStreamingMediaParser(ElementParser parent, String baseUri) {
super(parent, baseUri, TAG);

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.source.smoothstreaming.manifest;
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.source.smoothstreaming.offline;
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.source.smoothstreaming;
import com.google.android.exoplayer2.util.NonNullApi;

View file

@ -43,7 +43,8 @@ dependencies {
implementation 'androidx.media:media:1.0.1'
implementation 'androidx.annotation:annotation:1.1.0'
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
testImplementation project(modulePrefix + 'testutils-robolectric')
testImplementation project(modulePrefix + 'testutils')
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
}
ext {

View file

@ -39,11 +39,13 @@ android {
dependencies {
api 'org.mockito:mockito-core:' + mockitoVersion
api 'androidx.test:core:' + androidXTestVersion
api 'androidx.test.ext:junit:' + androidXTestVersion
api 'com.google.truth:truth:' + truthVersion
implementation 'androidx.annotation:annotation:1.1.0'
implementation project(modulePrefix + 'library-core')
implementation 'com.google.auto.value:auto-value-annotations:' + autoValueVersion
annotationProcessor 'com.google.auto.value:auto-value:' + autoValueVersion
testImplementation project(modulePrefix + 'testutils-robolectric')
testImplementation project(modulePrefix + 'testutils')
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
}

View file

@ -1,46 +0,0 @@
// 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.
apply from: '../constants.gradle'
apply plugin: 'com.android.library'
android {
compileSdkVersion project.ext.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
minSdkVersion project.ext.minSdkVersion
targetSdkVersion project.ext.targetSdkVersion
}
lintOptions {
// Robolectric depends on BouncyCastle, which depends on javax.naming,
// which is not part of Android.
disable 'InvalidPackage'
}
testOptions.unitTests.includeAndroidResources = true
}
dependencies {
api 'androidx.test:core:' + androidXTestVersion
api 'org.robolectric:robolectric:' + robolectricVersion
api project(modulePrefix + 'testutils')
implementation project(modulePrefix + 'library-core')
implementation 'androidx.annotation:annotation:1.1.0'
annotationProcessor 'com.google.auto.service:auto-service:' + autoServiceVersion
}

View file

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<manifest package="com.google.android.exoplayer2.testutil"/>