Refactor DashTest class
Moved DashHostedTest to top level classes. Added DashHostedTest.Builder. Move widevine offline tests to separate class with custom setUp and tearDown methods. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=146118310
This commit is contained in:
parent
8f482cb2ed
commit
af98ca661a
@ -0,0 +1,462 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer2.playbacktests.gts;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.C.WIDEVINE_UUID;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.Instrumentation;
|
||||||
|
import android.media.MediaDrm;
|
||||||
|
import android.media.UnsupportedSchemeException;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Surface;
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.DefaultLoadControl;
|
||||||
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
|
import com.google.android.exoplayer2.Format;
|
||||||
|
import com.google.android.exoplayer2.RendererCapabilities;
|
||||||
|
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||||
|
import com.google.android.exoplayer2.decoder.DecoderCounters;
|
||||||
|
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
|
||||||
|
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||||
|
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
|
||||||
|
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
|
||||||
|
import com.google.android.exoplayer2.drm.MediaDrmCallback;
|
||||||
|
import com.google.android.exoplayer2.drm.UnsupportedDrmException;
|
||||||
|
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
|
||||||
|
import com.google.android.exoplayer2.playbacktests.util.ActionSchedule;
|
||||||
|
import com.google.android.exoplayer2.playbacktests.util.DebugSimpleExoPlayer;
|
||||||
|
import com.google.android.exoplayer2.playbacktests.util.DecoderCountersUtil;
|
||||||
|
import com.google.android.exoplayer2.playbacktests.util.ExoHostedTest;
|
||||||
|
import com.google.android.exoplayer2.playbacktests.util.HostActivity;
|
||||||
|
import com.google.android.exoplayer2.playbacktests.util.HostActivity.HostedTest;
|
||||||
|
import com.google.android.exoplayer2.playbacktests.util.MetricsLogger;
|
||||||
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
|
import com.google.android.exoplayer2.source.TrackGroup;
|
||||||
|
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||||
|
import com.google.android.exoplayer2.source.dash.DashMediaSource;
|
||||||
|
import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource;
|
||||||
|
import com.google.android.exoplayer2.trackselection.FixedTrackSelection;
|
||||||
|
import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
|
||||||
|
import com.google.android.exoplayer2.trackselection.RandomTrackSelection;
|
||||||
|
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||||
|
import com.google.android.exoplayer2.upstream.BandwidthMeter;
|
||||||
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
|
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||||
|
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
|
||||||
|
import com.google.android.exoplayer2.upstream.TransferListener;
|
||||||
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import junit.framework.AssertionFailedError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link HostedTest} for DASH playback tests.
|
||||||
|
*/
|
||||||
|
@TargetApi(16)
|
||||||
|
public final class DashHostedTest extends ExoHostedTest {
|
||||||
|
|
||||||
|
/** {@link DashHostedTest} builder. */
|
||||||
|
public static final class Builder {
|
||||||
|
|
||||||
|
private static final long TEST_TIMEOUT_MS = 5 * 60 * 1000;
|
||||||
|
|
||||||
|
private static final String REPORT_NAME = "GtsExoPlayerTestCases";
|
||||||
|
private static final String REPORT_OBJECT_NAME = "playbacktest";
|
||||||
|
|
||||||
|
// Whether adaptive tests should enable video formats beyond those mandated by the Android CDD
|
||||||
|
// if the device advertises support for them.
|
||||||
|
private static final boolean ALLOW_ADDITIONAL_VIDEO_FORMATS = Util.SDK_INT >= 24;
|
||||||
|
|
||||||
|
private final String tag;
|
||||||
|
|
||||||
|
private String streamName;
|
||||||
|
private boolean fullPlaybackNoSeeking;
|
||||||
|
private String audioFormat;
|
||||||
|
private boolean canIncludeAdditionalVideoFormats;
|
||||||
|
private ActionSchedule actionSchedule;
|
||||||
|
private byte[] offlineLicenseKeySetId;
|
||||||
|
private String[] videoFormats;
|
||||||
|
private String manifestUrl;
|
||||||
|
private boolean useL1Widevine;
|
||||||
|
private String widevineLicenseUrl;
|
||||||
|
|
||||||
|
public Builder(String tag) {
|
||||||
|
this.tag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setStreamName(String streamName) {
|
||||||
|
this.streamName = streamName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setFullPlaybackNoSeeking(boolean fullPlaybackNoSeeking) {
|
||||||
|
this.fullPlaybackNoSeeking = fullPlaybackNoSeeking;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setCanIncludeAdditionalVideoFormats(
|
||||||
|
boolean canIncludeAdditionalVideoFormats) {
|
||||||
|
this.canIncludeAdditionalVideoFormats = canIncludeAdditionalVideoFormats
|
||||||
|
&& ALLOW_ADDITIONAL_VIDEO_FORMATS;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setActionSchedule(ActionSchedule actionSchedule) {
|
||||||
|
this.actionSchedule = actionSchedule;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setOfflineLicenseKeySetId(byte[] offlineLicenseKeySetId) {
|
||||||
|
this.offlineLicenseKeySetId = offlineLicenseKeySetId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setAudioVideoFormats(String audioFormat, String... videoFormats) {
|
||||||
|
this.audioFormat = audioFormat;
|
||||||
|
this.videoFormats = videoFormats;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setManifestUrl(String manifestUrl) {
|
||||||
|
this.manifestUrl = MANIFEST_URL_PREFIX + manifestUrl;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setManifestUrlForWidevine(String manifestUrl, String videoMimeType) {
|
||||||
|
this.useL1Widevine = isL1WidevineAvailable(videoMimeType);
|
||||||
|
this.manifestUrl = getWidevineManifestUrl(manifestUrl, useL1Widevine);
|
||||||
|
this.widevineLicenseUrl = getWidevineLicenseUrl(useL1Widevine);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DashHostedTest createDashHostedTest(boolean canIncludeAdditionalVideoFormats,
|
||||||
|
boolean isCddLimitedRetry, Instrumentation instrumentation) {
|
||||||
|
MetricsLogger metricsLogger = MetricsLogger.Factory.createDefault(instrumentation, tag,
|
||||||
|
REPORT_NAME, REPORT_OBJECT_NAME);
|
||||||
|
return new DashHostedTest(tag, streamName, manifestUrl, metricsLogger, fullPlaybackNoSeeking,
|
||||||
|
audioFormat, canIncludeAdditionalVideoFormats, isCddLimitedRetry, actionSchedule,
|
||||||
|
offlineLicenseKeySetId, widevineLicenseUrl, useL1Widevine, videoFormats);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runTest(HostActivity activity, Instrumentation instrumentation) {
|
||||||
|
DashHostedTest test = createDashHostedTest(canIncludeAdditionalVideoFormats, false,
|
||||||
|
instrumentation);
|
||||||
|
activity.runTest(test, TEST_TIMEOUT_MS);
|
||||||
|
// Retry test exactly once if adaptive test fails due to excessive dropped buffers when
|
||||||
|
// playing non-CDD required formats (b/28220076).
|
||||||
|
if (test.needsCddLimitedRetry) {
|
||||||
|
activity.runTest(createDashHostedTest(false, true, instrumentation), TEST_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String AUDIO_TAG_SUFFIX = ":Audio";
|
||||||
|
private static final String VIDEO_TAG_SUFFIX = ":Video";
|
||||||
|
static final int VIDEO_RENDERER_INDEX = 0;
|
||||||
|
static final int AUDIO_RENDERER_INDEX = 1;
|
||||||
|
|
||||||
|
private static final int MIN_LOADABLE_RETRY_COUNT = 10;
|
||||||
|
private static final int MAX_CONSECUTIVE_DROPPED_VIDEO_FRAMES = 10;
|
||||||
|
private static final float MAX_DROPPED_VIDEO_FRAME_FRACTION = 0.01f;
|
||||||
|
|
||||||
|
private static final String MANIFEST_URL_PREFIX = "https://storage.googleapis.com/exoplayer-test-"
|
||||||
|
+ "media-1/gen-3/screens/dash-vod-single-segment/";
|
||||||
|
|
||||||
|
private static final String WIDEVINE_L1_SUFFIX = "-hw.mpd";
|
||||||
|
private static final String WIDEVINE_L3_SUFFIX = "-sw.mpd";
|
||||||
|
|
||||||
|
private static final String WIDEVINE_LICENSE_URL =
|
||||||
|
"https://proxy.uat.widevine.com/proxy?provider=widevine_test&video_id=";
|
||||||
|
private static final String WIDEVINE_SW_CRYPTO_CONTENT_ID = "exoplayer_test_1";
|
||||||
|
private static final String WIDEVINE_HW_SECURE_DECODE_CONTENT_ID = "exoplayer_test_2";
|
||||||
|
private static final String WIDEVINE_SECURITY_LEVEL_1 = "L1";
|
||||||
|
private static final String WIDEVINE_SECURITY_LEVEL_3 = "L3";
|
||||||
|
private static final String SECURITY_LEVEL_PROPERTY = "securityLevel";
|
||||||
|
|
||||||
|
private final String streamName;
|
||||||
|
private final String manifestUrl;
|
||||||
|
private final MetricsLogger metricsLogger;
|
||||||
|
private final boolean fullPlaybackNoSeeking;
|
||||||
|
private final boolean isCddLimitedRetry;
|
||||||
|
private final DashTestTrackSelector trackSelector;
|
||||||
|
private final byte[] offlineLicenseKeySetId;
|
||||||
|
private final String widevineLicenseUrl;
|
||||||
|
private final boolean useL1Widevine;
|
||||||
|
|
||||||
|
boolean needsCddLimitedRetry;
|
||||||
|
|
||||||
|
public static String getWidevineManifestUrl(String manifestUrl, boolean useL1Widevine) {
|
||||||
|
return MANIFEST_URL_PREFIX + manifestUrl
|
||||||
|
+ (useL1Widevine ? WIDEVINE_L1_SUFFIX : WIDEVINE_L3_SUFFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getWidevineLicenseUrl(boolean useL1Widevine) {
|
||||||
|
return WIDEVINE_LICENSE_URL
|
||||||
|
+ (useL1Widevine ? WIDEVINE_HW_SECURE_DECODE_CONTENT_ID : WIDEVINE_SW_CRYPTO_CONTENT_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(18)
|
||||||
|
@SuppressWarnings("ResourceType")
|
||||||
|
public static boolean isL1WidevineAvailable(String videoMimeType) {
|
||||||
|
try {
|
||||||
|
// Force L3 if secure decoder is not available.
|
||||||
|
if (MediaCodecUtil.getDecoderInfo(videoMimeType, true) == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaDrm mediaDrm = new MediaDrm(WIDEVINE_UUID);
|
||||||
|
String securityProperty = mediaDrm.getPropertyString(SECURITY_LEVEL_PROPERTY);
|
||||||
|
mediaDrm.release();
|
||||||
|
return WIDEVINE_SECURITY_LEVEL_1.equals(securityProperty);
|
||||||
|
} catch (MediaCodecUtil.DecoderQueryException | UnsupportedSchemeException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param tag A tag to use for logging.
|
||||||
|
* @param streamName The name of the test stream for metric logging.
|
||||||
|
* @param manifestUrl The manifest url.
|
||||||
|
* @param metricsLogger Logger to log metrics from the test.
|
||||||
|
* @param fullPlaybackNoSeeking Whether the test will play the entire source with no seeking.
|
||||||
|
* @param audioFormat The audio format.
|
||||||
|
* @param canIncludeAdditionalVideoFormats Whether to use video formats in addition to those
|
||||||
|
* listed in the videoFormats argument, if the device is capable of playing them.
|
||||||
|
* @param isCddLimitedRetry Whether this is a CDD limited retry following a previous failure.
|
||||||
|
* @param actionSchedule The action schedule for the test.
|
||||||
|
* @param offlineLicenseKeySetId The key set id of the license to be used.
|
||||||
|
* @param widevineLicenseUrl If the video is Widevine encrypted, this is the license url
|
||||||
|
* otherwise null.
|
||||||
|
* @param useL1Widevine Whether to use L1 Widevine.
|
||||||
|
* @param videoFormats The video formats.
|
||||||
|
*/
|
||||||
|
private DashHostedTest(String tag, String streamName, String manifestUrl,
|
||||||
|
MetricsLogger metricsLogger, boolean fullPlaybackNoSeeking, String audioFormat,
|
||||||
|
boolean canIncludeAdditionalVideoFormats, boolean isCddLimitedRetry,
|
||||||
|
ActionSchedule actionSchedule, byte[] offlineLicenseKeySetId, String widevineLicenseUrl,
|
||||||
|
boolean useL1Widevine, String... videoFormats) {
|
||||||
|
super(tag, fullPlaybackNoSeeking);
|
||||||
|
Assertions.checkArgument(!(isCddLimitedRetry && canIncludeAdditionalVideoFormats));
|
||||||
|
this.streamName = streamName;
|
||||||
|
this.manifestUrl = manifestUrl;
|
||||||
|
this.metricsLogger = metricsLogger;
|
||||||
|
this.fullPlaybackNoSeeking = fullPlaybackNoSeeking;
|
||||||
|
this.isCddLimitedRetry = isCddLimitedRetry;
|
||||||
|
this.offlineLicenseKeySetId = offlineLicenseKeySetId;
|
||||||
|
this.widevineLicenseUrl = widevineLicenseUrl;
|
||||||
|
this.useL1Widevine = useL1Widevine;
|
||||||
|
trackSelector = new DashTestTrackSelector(tag, audioFormat, videoFormats,
|
||||||
|
canIncludeAdditionalVideoFormats);
|
||||||
|
if (actionSchedule != null) {
|
||||||
|
setSchedule(actionSchedule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MappingTrackSelector buildTrackSelector(HostActivity host,
|
||||||
|
BandwidthMeter bandwidthMeter) {
|
||||||
|
return trackSelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DefaultDrmSessionManager<FrameworkMediaCrypto> buildDrmSessionManager(
|
||||||
|
final String userAgent) {
|
||||||
|
if (widevineLicenseUrl == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
MediaDrmCallback drmCallback = new HttpMediaDrmCallback(widevineLicenseUrl,
|
||||||
|
new DefaultHttpDataSourceFactory(userAgent));
|
||||||
|
DefaultDrmSessionManager<FrameworkMediaCrypto> drmSessionManager =
|
||||||
|
DefaultDrmSessionManager.newWidevineInstance(drmCallback, null, null, null);
|
||||||
|
if (!useL1Widevine) {
|
||||||
|
drmSessionManager.setPropertyString(
|
||||||
|
SECURITY_LEVEL_PROPERTY, WIDEVINE_SECURITY_LEVEL_3);
|
||||||
|
}
|
||||||
|
if (offlineLicenseKeySetId != null) {
|
||||||
|
drmSessionManager.setMode(DefaultDrmSessionManager.MODE_PLAYBACK,
|
||||||
|
offlineLicenseKeySetId);
|
||||||
|
}
|
||||||
|
return drmSessionManager;
|
||||||
|
} catch (UnsupportedDrmException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SimpleExoPlayer buildExoPlayer(HostActivity host, Surface surface,
|
||||||
|
MappingTrackSelector trackSelector,
|
||||||
|
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
|
||||||
|
SimpleExoPlayer player = new DebugSimpleExoPlayer(host, trackSelector,
|
||||||
|
new DefaultLoadControl(), drmSessionManager);
|
||||||
|
player.setVideoSurface(surface);
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MediaSource buildSource(HostActivity host, String userAgent,
|
||||||
|
TransferListener<? super DataSource> mediaTransferListener) {
|
||||||
|
DataSource.Factory manifestDataSourceFactory = new DefaultDataSourceFactory(host, userAgent);
|
||||||
|
DataSource.Factory mediaDataSourceFactory = new DefaultDataSourceFactory(host, userAgent,
|
||||||
|
mediaTransferListener);
|
||||||
|
Uri manifestUri = Uri.parse(manifestUrl);
|
||||||
|
DefaultDashChunkSource.Factory chunkSourceFactory = new DefaultDashChunkSource.Factory(
|
||||||
|
mediaDataSourceFactory);
|
||||||
|
return new DashMediaSource(manifestUri, manifestDataSourceFactory, chunkSourceFactory,
|
||||||
|
MIN_LOADABLE_RETRY_COUNT, 0 /* livePresentationDelayMs */, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void logMetrics(DecoderCounters audioCounters, DecoderCounters videoCounters) {
|
||||||
|
metricsLogger.logMetric(MetricsLogger.KEY_TEST_NAME, streamName);
|
||||||
|
metricsLogger.logMetric(MetricsLogger.KEY_IS_CDD_LIMITED_RETRY, isCddLimitedRetry);
|
||||||
|
metricsLogger.logMetric(MetricsLogger.KEY_FRAMES_DROPPED_COUNT,
|
||||||
|
videoCounters.droppedOutputBufferCount);
|
||||||
|
metricsLogger.logMetric(MetricsLogger.KEY_MAX_CONSECUTIVE_FRAMES_DROPPED_COUNT,
|
||||||
|
videoCounters.maxConsecutiveDroppedOutputBufferCount);
|
||||||
|
metricsLogger.logMetric(MetricsLogger.KEY_FRAMES_SKIPPED_COUNT,
|
||||||
|
videoCounters.skippedOutputBufferCount);
|
||||||
|
metricsLogger.logMetric(MetricsLogger.KEY_FRAMES_RENDERED_COUNT,
|
||||||
|
videoCounters.renderedOutputBufferCount);
|
||||||
|
metricsLogger.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assertPassed(DecoderCounters audioCounters, DecoderCounters videoCounters) {
|
||||||
|
if (fullPlaybackNoSeeking) {
|
||||||
|
// We shouldn't have skipped any output buffers.
|
||||||
|
DecoderCountersUtil.assertSkippedOutputBufferCount(tag + AUDIO_TAG_SUFFIX, audioCounters, 0);
|
||||||
|
DecoderCountersUtil.assertSkippedOutputBufferCount(tag + VIDEO_TAG_SUFFIX, videoCounters, 0);
|
||||||
|
// We allow one fewer output buffer due to the way that MediaCodecRenderer and the
|
||||||
|
// underlying decoders handle the end of stream. This should be tightened up in the future.
|
||||||
|
DecoderCountersUtil.assertTotalOutputBufferCount(tag + AUDIO_TAG_SUFFIX, audioCounters,
|
||||||
|
audioCounters.inputBufferCount - 1, audioCounters.inputBufferCount);
|
||||||
|
DecoderCountersUtil.assertTotalOutputBufferCount(tag + VIDEO_TAG_SUFFIX, videoCounters,
|
||||||
|
videoCounters.inputBufferCount - 1, videoCounters.inputBufferCount);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
int droppedFrameLimit = (int) Math.ceil(MAX_DROPPED_VIDEO_FRAME_FRACTION
|
||||||
|
* DecoderCountersUtil.getTotalOutputBuffers(videoCounters));
|
||||||
|
// Assert that performance is acceptable.
|
||||||
|
// Assert that total dropped frames were within limit.
|
||||||
|
DecoderCountersUtil.assertDroppedOutputBufferLimit(tag + VIDEO_TAG_SUFFIX, videoCounters,
|
||||||
|
droppedFrameLimit);
|
||||||
|
// Assert that consecutive dropped frames were within limit.
|
||||||
|
DecoderCountersUtil.assertConsecutiveDroppedOutputBufferLimit(tag + VIDEO_TAG_SUFFIX,
|
||||||
|
videoCounters, MAX_CONSECUTIVE_DROPPED_VIDEO_FRAMES);
|
||||||
|
} catch (AssertionFailedError e) {
|
||||||
|
if (trackSelector.includedAdditionalVideoFormats) {
|
||||||
|
// Retry limiting to CDD mandated formats (b/28220076).
|
||||||
|
Log.e(tag, "Too many dropped or consecutive dropped frames.", e);
|
||||||
|
needsCddLimitedRetry = true;
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class DashTestTrackSelector extends MappingTrackSelector {
|
||||||
|
|
||||||
|
private final String tag;
|
||||||
|
private final String audioFormatId;
|
||||||
|
private final String[] videoFormatIds;
|
||||||
|
private final boolean canIncludeAdditionalVideoFormats;
|
||||||
|
|
||||||
|
public boolean includedAdditionalVideoFormats;
|
||||||
|
|
||||||
|
private DashTestTrackSelector(String tag, String audioFormatId, String[] videoFormatIds,
|
||||||
|
boolean canIncludeAdditionalVideoFormats) {
|
||||||
|
this.tag = tag;
|
||||||
|
this.audioFormatId = audioFormatId;
|
||||||
|
this.videoFormatIds = videoFormatIds;
|
||||||
|
this.canIncludeAdditionalVideoFormats = canIncludeAdditionalVideoFormats;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TrackSelection[] selectTracks(RendererCapabilities[] rendererCapabilities,
|
||||||
|
TrackGroupArray[] rendererTrackGroupArrays, int[][][] rendererFormatSupports)
|
||||||
|
throws ExoPlaybackException {
|
||||||
|
Assertions.checkState(rendererCapabilities[VIDEO_RENDERER_INDEX].getTrackType()
|
||||||
|
== C.TRACK_TYPE_VIDEO);
|
||||||
|
Assertions.checkState(rendererCapabilities[AUDIO_RENDERER_INDEX].getTrackType()
|
||||||
|
== C.TRACK_TYPE_AUDIO);
|
||||||
|
Assertions.checkState(rendererTrackGroupArrays[VIDEO_RENDERER_INDEX].length == 1);
|
||||||
|
Assertions.checkState(rendererTrackGroupArrays[AUDIO_RENDERER_INDEX].length == 1);
|
||||||
|
TrackSelection[] selections = new TrackSelection[rendererCapabilities.length];
|
||||||
|
selections[VIDEO_RENDERER_INDEX] = new RandomTrackSelection(
|
||||||
|
rendererTrackGroupArrays[VIDEO_RENDERER_INDEX].get(0),
|
||||||
|
getVideoTrackIndices(rendererTrackGroupArrays[VIDEO_RENDERER_INDEX].get(0),
|
||||||
|
rendererFormatSupports[VIDEO_RENDERER_INDEX][0], videoFormatIds,
|
||||||
|
canIncludeAdditionalVideoFormats),
|
||||||
|
0 /* seed */);
|
||||||
|
selections[AUDIO_RENDERER_INDEX] = new FixedTrackSelection(
|
||||||
|
rendererTrackGroupArrays[AUDIO_RENDERER_INDEX].get(0),
|
||||||
|
getTrackIndex(rendererTrackGroupArrays[AUDIO_RENDERER_INDEX].get(0), audioFormatId));
|
||||||
|
includedAdditionalVideoFormats =
|
||||||
|
selections[VIDEO_RENDERER_INDEX].length() > videoFormatIds.length;
|
||||||
|
return selections;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] getVideoTrackIndices(TrackGroup trackGroup, int[] formatSupport,
|
||||||
|
String[] formatIds, boolean canIncludeAdditionalFormats) {
|
||||||
|
List<Integer> trackIndices = new ArrayList<>();
|
||||||
|
|
||||||
|
// Always select explicitly listed representations.
|
||||||
|
for (String formatId : formatIds) {
|
||||||
|
int trackIndex = getTrackIndex(trackGroup, formatId);
|
||||||
|
Log.d(tag, "Adding base video format: "
|
||||||
|
+ Format.toLogString(trackGroup.getFormat(trackIndex)));
|
||||||
|
trackIndices.add(trackIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select additional video representations, if supported by the device.
|
||||||
|
if (canIncludeAdditionalFormats) {
|
||||||
|
for (int i = 0; i < trackGroup.length; i++) {
|
||||||
|
if (!trackIndices.contains(i) && isFormatHandled(formatSupport[i])) {
|
||||||
|
Log.d(tag, "Adding extra video format: "
|
||||||
|
+ Format.toLogString(trackGroup.getFormat(i)));
|
||||||
|
trackIndices.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] trackIndicesArray = Util.toArray(trackIndices);
|
||||||
|
Arrays.sort(trackIndicesArray);
|
||||||
|
return trackIndicesArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getTrackIndex(TrackGroup trackGroup, String formatId) {
|
||||||
|
for (int i = 0; i < trackGroup.length; i++) {
|
||||||
|
if (trackGroup.getFormat(i).id.equals(formatId)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Format " + formatId + " not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isFormatHandled(int formatSupport) {
|
||||||
|
return (formatSupport & RendererCapabilities.FORMAT_SUPPORT_MASK)
|
||||||
|
== RendererCapabilities.FORMAT_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer2.playbacktests.gts;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test data for {@link DashTest} and {@link DashWidevineOfflineTest).
|
||||||
|
*/
|
||||||
|
public final class DashTestData {
|
||||||
|
|
||||||
|
// Clear content manifests.
|
||||||
|
public static final String H264_MANIFEST = "manifest-h264.mpd";
|
||||||
|
public static final String H265_MANIFEST = "manifest-h265.mpd";
|
||||||
|
public static final String VP9_MANIFEST = "manifest-vp9.mpd";
|
||||||
|
public static final String H264_23_MANIFEST = "manifest-h264-23.mpd";
|
||||||
|
public static final String H264_24_MANIFEST = "manifest-h264-24.mpd";
|
||||||
|
public static final String H264_29_MANIFEST = "manifest-h264-29.mpd";
|
||||||
|
// Widevine encrypted content manifests.
|
||||||
|
public static final String WIDEVINE_H264_MANIFEST_PREFIX = "manifest-h264-enc";
|
||||||
|
public static final String WIDEVINE_H265_MANIFEST_PREFIX = "manifest-h265-enc";
|
||||||
|
public static final String WIDEVINE_VP9_MANIFEST_PREFIX = "manifest-vp9-enc";
|
||||||
|
public static final String WIDEVINE_H264_23_MANIFEST_PREFIX = "manifest-h264-23-enc";
|
||||||
|
public static final String WIDEVINE_H264_24_MANIFEST_PREFIX = "manifest-h264-24-enc";
|
||||||
|
public static final String WIDEVINE_H264_29_MANIFEST_PREFIX = "manifest-h264-29-enc";
|
||||||
|
|
||||||
|
public static final String AAC_AUDIO_REPRESENTATION_ID = "141";
|
||||||
|
public static final String H264_BASELINE_240P_VIDEO_REPRESENTATION_ID = "avc-baseline-240";
|
||||||
|
public static final String H264_BASELINE_480P_VIDEO_REPRESENTATION_ID = "avc-baseline-480";
|
||||||
|
public static final String H264_MAIN_240P_VIDEO_REPRESENTATION_ID = "avc-main-240";
|
||||||
|
public static final String H264_MAIN_480P_VIDEO_REPRESENTATION_ID = "avc-main-480";
|
||||||
|
// The highest quality H264 format mandated by the Android CDD.
|
||||||
|
public static final String H264_CDD_FIXED = Util.SDK_INT < 23
|
||||||
|
? H264_BASELINE_480P_VIDEO_REPRESENTATION_ID : H264_MAIN_480P_VIDEO_REPRESENTATION_ID;
|
||||||
|
// Multiple H264 formats mandated by the Android CDD. Note: The CDD actually mandated main profile
|
||||||
|
// support from API level 23, but we opt to test only from 24 due to known issues on API level 23
|
||||||
|
// when switching between baseline and main profiles on certain devices.
|
||||||
|
public static final String[] H264_CDD_ADAPTIVE = Util.SDK_INT < 24
|
||||||
|
? new String[] {
|
||||||
|
H264_BASELINE_240P_VIDEO_REPRESENTATION_ID,
|
||||||
|
H264_BASELINE_480P_VIDEO_REPRESENTATION_ID}
|
||||||
|
: new String[] {
|
||||||
|
H264_BASELINE_240P_VIDEO_REPRESENTATION_ID,
|
||||||
|
H264_BASELINE_480P_VIDEO_REPRESENTATION_ID,
|
||||||
|
H264_MAIN_240P_VIDEO_REPRESENTATION_ID,
|
||||||
|
H264_MAIN_480P_VIDEO_REPRESENTATION_ID};
|
||||||
|
|
||||||
|
public static final String H264_BASELINE_480P_23FPS_VIDEO_REPRESENTATION_ID =
|
||||||
|
"avc-baseline-480-23";
|
||||||
|
public static final String H264_BASELINE_480P_24FPS_VIDEO_REPRESENTATION_ID =
|
||||||
|
"avc-baseline-480-24";
|
||||||
|
public static final String H264_BASELINE_480P_29FPS_VIDEO_REPRESENTATION_ID =
|
||||||
|
"avc-baseline-480-29";
|
||||||
|
|
||||||
|
public static final String H265_BASELINE_288P_VIDEO_REPRESENTATION_ID = "hevc-main-288";
|
||||||
|
public static final String H265_BASELINE_360P_VIDEO_REPRESENTATION_ID = "hevc-main-360";
|
||||||
|
// The highest quality H265 format mandated by the Android CDD.
|
||||||
|
public static final String H265_CDD_FIXED = H265_BASELINE_360P_VIDEO_REPRESENTATION_ID;
|
||||||
|
// Multiple H265 formats mandated by the Android CDD.
|
||||||
|
public static final String[] H265_CDD_ADAPTIVE =
|
||||||
|
new String[] {
|
||||||
|
H265_BASELINE_288P_VIDEO_REPRESENTATION_ID,
|
||||||
|
H265_BASELINE_360P_VIDEO_REPRESENTATION_ID};
|
||||||
|
|
||||||
|
public static final String VORBIS_AUDIO_REPRESENTATION_ID = "4";
|
||||||
|
public static final String VP9_180P_VIDEO_REPRESENTATION_ID = "0";
|
||||||
|
public static final String VP9_360P_VIDEO_REPRESENTATION_ID = "1";
|
||||||
|
// The highest quality VP9 format mandated by the Android CDD.
|
||||||
|
public static final String VP9_CDD_FIXED = VP9_360P_VIDEO_REPRESENTATION_ID;
|
||||||
|
// Multiple VP9 formats mandated by the Android CDD.
|
||||||
|
public static final String[] VP9_CDD_ADAPTIVE =
|
||||||
|
new String[] {
|
||||||
|
VP9_180P_VIDEO_REPRESENTATION_ID,
|
||||||
|
VP9_360P_VIDEO_REPRESENTATION_ID};
|
||||||
|
|
||||||
|
// Widevine encrypted content representation ids.
|
||||||
|
public static final String WIDEVINE_AAC_AUDIO_REPRESENTATION_ID = "0";
|
||||||
|
public static final String WIDEVINE_H264_BASELINE_240P_VIDEO_REPRESENTATION_ID = "1";
|
||||||
|
public static final String WIDEVINE_H264_BASELINE_480P_VIDEO_REPRESENTATION_ID = "2";
|
||||||
|
public static final String WIDEVINE_H264_MAIN_240P_VIDEO_REPRESENTATION_ID = "3";
|
||||||
|
public static final String WIDEVINE_H264_MAIN_480P_VIDEO_REPRESENTATION_ID = "4";
|
||||||
|
// The highest quality H264 format mandated by the Android CDD.
|
||||||
|
public static final String WIDEVINE_H264_CDD_FIXED = Util.SDK_INT < 23
|
||||||
|
? WIDEVINE_H264_BASELINE_480P_VIDEO_REPRESENTATION_ID
|
||||||
|
: WIDEVINE_H264_MAIN_480P_VIDEO_REPRESENTATION_ID;
|
||||||
|
// Multiple H264 formats mandated by the Android CDD. Note: The CDD actually mandated main profile
|
||||||
|
// support from API level 23, but we opt to test only from 24 due to known issues on API level 23
|
||||||
|
// when switching between baseline and main profiles on certain devices.
|
||||||
|
public static final String[] WIDEVINE_H264_CDD_ADAPTIVE = Util.SDK_INT < 24
|
||||||
|
? new String[] {
|
||||||
|
WIDEVINE_H264_BASELINE_240P_VIDEO_REPRESENTATION_ID,
|
||||||
|
WIDEVINE_H264_BASELINE_480P_VIDEO_REPRESENTATION_ID}
|
||||||
|
: new String[] {
|
||||||
|
WIDEVINE_H264_BASELINE_240P_VIDEO_REPRESENTATION_ID,
|
||||||
|
WIDEVINE_H264_BASELINE_480P_VIDEO_REPRESENTATION_ID,
|
||||||
|
WIDEVINE_H264_MAIN_240P_VIDEO_REPRESENTATION_ID,
|
||||||
|
WIDEVINE_H264_MAIN_480P_VIDEO_REPRESENTATION_ID};
|
||||||
|
|
||||||
|
public static final String WIDEVINE_H264_BASELINE_480P_23FPS_VIDEO_REPRESENTATION_ID = "2";
|
||||||
|
public static final String WIDEVINE_H264_BASELINE_480P_24FPS_VIDEO_REPRESENTATION_ID = "2";
|
||||||
|
public static final String WIDEVINE_H264_BASELINE_480P_29FPS_VIDEO_REPRESENTATION_ID = "2";
|
||||||
|
|
||||||
|
public static final String WIDEVINE_H265_BASELINE_288P_VIDEO_REPRESENTATION_ID = "1";
|
||||||
|
public static final String WIDEVINE_H265_BASELINE_360P_VIDEO_REPRESENTATION_ID = "2";
|
||||||
|
// The highest quality H265 format mandated by the Android CDD.
|
||||||
|
public static final String WIDEVINE_H265_CDD_FIXED =
|
||||||
|
WIDEVINE_H265_BASELINE_360P_VIDEO_REPRESENTATION_ID;
|
||||||
|
// Multiple H265 formats mandated by the Android CDD.
|
||||||
|
public static final String[] WIDEVINE_H265_CDD_ADAPTIVE =
|
||||||
|
new String[] {
|
||||||
|
WIDEVINE_H265_BASELINE_288P_VIDEO_REPRESENTATION_ID,
|
||||||
|
WIDEVINE_H265_BASELINE_360P_VIDEO_REPRESENTATION_ID};
|
||||||
|
|
||||||
|
public static final String WIDEVINE_VORBIS_AUDIO_REPRESENTATION_ID = "0";
|
||||||
|
public static final String WIDEVINE_VP9_180P_VIDEO_REPRESENTATION_ID = "1";
|
||||||
|
public static final String WIDEVINE_VP9_360P_VIDEO_REPRESENTATION_ID = "2";
|
||||||
|
// The highest quality VP9 format mandated by the Android CDD.
|
||||||
|
public static final String WIDEVINE_VP9_CDD_FIXED = VP9_360P_VIDEO_REPRESENTATION_ID;
|
||||||
|
// Multiple VP9 formats mandated by the Android CDD.
|
||||||
|
public static final String[] WIDEVINE_VP9_CDD_ADAPTIVE =
|
||||||
|
new String[] {
|
||||||
|
WIDEVINE_VP9_180P_VIDEO_REPRESENTATION_ID,
|
||||||
|
WIDEVINE_VP9_360P_VIDEO_REPRESENTATION_ID};
|
||||||
|
|
||||||
|
private DashTestData() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer2.playbacktests.gts;
|
||||||
|
|
||||||
|
import android.media.MediaDrm.MediaDrmStateException;
|
||||||
|
import android.test.ActivityInstrumentationTestCase2;
|
||||||
|
import android.util.Pair;
|
||||||
|
import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
|
||||||
|
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
|
||||||
|
import com.google.android.exoplayer2.drm.OfflineLicenseHelper;
|
||||||
|
import com.google.android.exoplayer2.playbacktests.util.ActionSchedule;
|
||||||
|
import com.google.android.exoplayer2.playbacktests.util.HostActivity;
|
||||||
|
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
import java.io.IOException;
|
||||||
|
import junit.framework.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests Widevine encrypted DASH playbacks using offline keys.
|
||||||
|
*/
|
||||||
|
public final class DashWidevineOfflineTest extends ActivityInstrumentationTestCase2<HostActivity> {
|
||||||
|
|
||||||
|
private static final String TAG = "DashWidevineOfflineTest";
|
||||||
|
private static final String USER_AGENT = "ExoPlayerPlaybackTests";
|
||||||
|
|
||||||
|
private DashHostedTest.Builder builder;
|
||||||
|
private String widevineManifestUrl;
|
||||||
|
private DefaultHttpDataSourceFactory httpDataSourceFactory;
|
||||||
|
private OfflineLicenseHelper<FrameworkMediaCrypto> offlineLicenseHelper;
|
||||||
|
private byte[] offlineLicenseKeySetId;
|
||||||
|
|
||||||
|
public DashWidevineOfflineTest() {
|
||||||
|
super(HostActivity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
builder = new DashHostedTest.Builder(TAG)
|
||||||
|
.setStreamName("test_widevine_h264_fixed_offline")
|
||||||
|
.setManifestUrlForWidevine(DashTestData.WIDEVINE_H264_MANIFEST_PREFIX, MimeTypes.VIDEO_H264)
|
||||||
|
.setFullPlaybackNoSeeking(true)
|
||||||
|
.setCanIncludeAdditionalVideoFormats(false)
|
||||||
|
.setAudioVideoFormats(DashTestData.WIDEVINE_AAC_AUDIO_REPRESENTATION_ID,
|
||||||
|
DashTestData.WIDEVINE_H264_CDD_FIXED);
|
||||||
|
|
||||||
|
boolean useL1Widevine = DashHostedTest.isL1WidevineAvailable(MimeTypes.VIDEO_H264);
|
||||||
|
widevineManifestUrl = DashHostedTest
|
||||||
|
.getWidevineManifestUrl(DashTestData.WIDEVINE_H264_MANIFEST_PREFIX, useL1Widevine);
|
||||||
|
String widevineLicenseUrl = DashHostedTest.getWidevineLicenseUrl(useL1Widevine);
|
||||||
|
httpDataSourceFactory = new DefaultHttpDataSourceFactory(USER_AGENT);
|
||||||
|
offlineLicenseHelper = OfflineLicenseHelper.newWidevineInstance(widevineLicenseUrl,
|
||||||
|
httpDataSourceFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
if (offlineLicenseKeySetId != null) {
|
||||||
|
releaseLicense();
|
||||||
|
}
|
||||||
|
if (offlineLicenseHelper != null) {
|
||||||
|
offlineLicenseHelper.releaseResources();
|
||||||
|
}
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offline license tests
|
||||||
|
|
||||||
|
public void testWidevineOfflineLicense() throws Exception {
|
||||||
|
if (Util.SDK_INT < 22) {
|
||||||
|
return; // Pass.
|
||||||
|
}
|
||||||
|
downloadLicense();
|
||||||
|
builder.runTest(getActivity(), getInstrumentation());
|
||||||
|
|
||||||
|
// Renew license after playback should still work
|
||||||
|
offlineLicenseKeySetId = offlineLicenseHelper.renew(offlineLicenseKeySetId);
|
||||||
|
Assert.assertNotNull(offlineLicenseKeySetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWidevineOfflineReleasedLicense() throws Throwable {
|
||||||
|
if (Util.SDK_INT < 22) {
|
||||||
|
return; // Pass.
|
||||||
|
}
|
||||||
|
downloadLicense();
|
||||||
|
releaseLicense(); // keySetId no longer valid.
|
||||||
|
|
||||||
|
try {
|
||||||
|
builder.runTest(getActivity(), getInstrumentation());
|
||||||
|
fail("Playback should fail because the license has been released.");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// Get the root cause
|
||||||
|
while (true) {
|
||||||
|
Throwable cause = e.getCause();
|
||||||
|
if (cause == null || cause == e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
e = cause;
|
||||||
|
}
|
||||||
|
// It should be a MediaDrmStateException instance
|
||||||
|
if (!(e instanceof MediaDrmStateException)) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWidevineOfflineExpiredLicense() throws Exception {
|
||||||
|
if (Util.SDK_INT < 22) {
|
||||||
|
return; // Pass.
|
||||||
|
}
|
||||||
|
downloadLicense();
|
||||||
|
|
||||||
|
// Wait until the license expires
|
||||||
|
long licenseDuration =
|
||||||
|
offlineLicenseHelper.getLicenseDurationRemainingSec(offlineLicenseKeySetId).first;
|
||||||
|
assertTrue("License duration should be less than 30 sec. "
|
||||||
|
+ "Server settings might have changed.", licenseDuration < 30);
|
||||||
|
while (licenseDuration > 0) {
|
||||||
|
synchronized (this) {
|
||||||
|
wait(licenseDuration * 1000 + 2000);
|
||||||
|
}
|
||||||
|
long previousDuration = licenseDuration;
|
||||||
|
licenseDuration =
|
||||||
|
offlineLicenseHelper.getLicenseDurationRemainingSec(offlineLicenseKeySetId).first;
|
||||||
|
assertTrue("License duration should be decreasing.", previousDuration > licenseDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultDrmSessionManager should renew the license and stream play fine
|
||||||
|
builder.runTest(getActivity(), getInstrumentation());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWidevineOfflineLicenseExpiresOnPause() throws Exception {
|
||||||
|
if (Util.SDK_INT < 22) {
|
||||||
|
return; // Pass.
|
||||||
|
}
|
||||||
|
downloadLicense();
|
||||||
|
|
||||||
|
// During playback pause until the license expires then continue playback
|
||||||
|
Pair<Long, Long> licenseDurationRemainingSec =
|
||||||
|
offlineLicenseHelper.getLicenseDurationRemainingSec(offlineLicenseKeySetId);
|
||||||
|
long licenseDuration = licenseDurationRemainingSec.first;
|
||||||
|
assertTrue("License duration should be less than 30 sec. "
|
||||||
|
+ "Server settings might have changed.", licenseDuration < 30);
|
||||||
|
ActionSchedule schedule = new ActionSchedule.Builder(TAG)
|
||||||
|
.delay(3000).pause().delay(licenseDuration * 1000 + 2000).play().build();
|
||||||
|
|
||||||
|
// DefaultDrmSessionManager should renew the license and stream play fine
|
||||||
|
builder
|
||||||
|
.setActionSchedule(schedule)
|
||||||
|
.runTest(getActivity(), getInstrumentation());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void downloadLicense() throws InterruptedException, DrmSessionException, IOException {
|
||||||
|
offlineLicenseKeySetId = offlineLicenseHelper.download(
|
||||||
|
httpDataSourceFactory.createDataSource(), widevineManifestUrl);
|
||||||
|
Assert.assertNotNull(offlineLicenseKeySetId);
|
||||||
|
Assert.assertTrue(offlineLicenseKeySetId.length > 0);
|
||||||
|
builder.setOfflineLicenseKeySetId(offlineLicenseKeySetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void releaseLicense() throws DrmSessionException {
|
||||||
|
offlineLicenseHelper.release(offlineLicenseKeySetId);
|
||||||
|
offlineLicenseKeySetId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -63,7 +63,8 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
|
|||||||
public static final long EXPECTED_PLAYING_TIME_MEDIA_DURATION_MS = -2;
|
public static final long EXPECTED_PLAYING_TIME_MEDIA_DURATION_MS = -2;
|
||||||
public static final long EXPECTED_PLAYING_TIME_UNSET = -1;
|
public static final long EXPECTED_PLAYING_TIME_UNSET = -1;
|
||||||
|
|
||||||
private final String tag;
|
protected final String tag;
|
||||||
|
|
||||||
private final boolean failOnPlayerError;
|
private final boolean failOnPlayerError;
|
||||||
private final long expectedPlayingTimeMs;
|
private final long expectedPlayingTimeMs;
|
||||||
private final DecoderCounters videoDecoderCounters;
|
private final DecoderCounters videoDecoderCounters;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user