Search in sources :

Example 11 with CacheDataSource

use of com.google.android.exoplayer2.upstream.cache.CacheDataSource in project ExoPlayer by google.

the class CacheDataSourceTest method switchToCacheSourceWithNonBlockingCacheDataSource.

@Test
public void switchToCacheSourceWithNonBlockingCacheDataSource() throws Exception {
    // Create a fake data source with a 1 MB default data.
    FakeDataSource upstream = new FakeDataSource();
    FakeData fakeData = upstream.getDataSet().newDefaultData().appendReadData(1024 * 1024 - 1);
    // Insert an action just before the end of the data to fail the test if reading from upstream
    // reaches end of the data.
    fakeData.appendReadAction(() -> fail("Read from upstream shouldn't reach to the end of the data.")).appendReadData(1);
    // Lock the content on the cache.
    CacheSpan cacheSpan = cache.startReadWriteNonBlocking(defaultCacheKey, 0, C.LENGTH_UNSET);
    assertThat(cacheSpan).isNotNull();
    assertThat(cacheSpan.isHoleSpan()).isTrue();
    // Create non blocking CacheDataSource.
    CacheDataSource cacheDataSource = new CacheDataSource(cache, upstream, 0);
    // Open source and read some data from upstream without writing to cache as the data is locked.
    cacheDataSource.open(unboundedDataSpec);
    byte[] buffer = new byte[1024];
    cacheDataSource.read(buffer, 0, buffer.length);
    // Unlock the span.
    cache.releaseHoleSpan(cacheSpan);
    assertCacheEmpty(cache);
    // Cache the data. Although we use another FakeDataSource instance, it shouldn't matter.
    FakeDataSource upstream2 = new FakeDataSource(new FakeDataSource().getDataSet().newDefaultData().appendReadData(1024 * 1024).endData());
    CacheWriter cacheWriter = new CacheWriter(new CacheDataSource(cache, upstream2), unboundedDataSpec, /* temporaryBuffer= */
    null, /* progressListener= */
    null);
    cacheWriter.cache();
    // Read the rest of the data.
    DataSourceUtil.readToEnd(cacheDataSource);
    cacheDataSource.close();
}
Also used : FakeDataSource(com.google.android.exoplayer2.testutil.FakeDataSource) FakeData(com.google.android.exoplayer2.testutil.FakeDataSet.FakeData) Test(org.junit.Test)

Example 12 with CacheDataSource

use of com.google.android.exoplayer2.upstream.cache.CacheDataSource in project ExoPlayer by google.

the class CacheDataSourceTest method deleteCachedWhileReadingFromUpstreamWithBlockingCacheDataSourceDoesNotBlock.

@Test
public void deleteCachedWhileReadingFromUpstreamWithBlockingCacheDataSourceDoesNotBlock() throws Exception {
    // Create a fake data source with a 1 KB default data.
    FakeDataSource upstream = new FakeDataSource();
    int dataLength = 1024;
    upstream.getDataSet().newDefaultData().appendReadData(dataLength).endData();
    // Cache the latter half of the data.
    int halfDataLength = 512;
    DataSpec dataSpec = buildDataSpec(/* position= */
    0, halfDataLength);
    CacheWriter cacheWriter = new CacheWriter(new CacheDataSource(cache, upstream), dataSpec, /* temporaryBuffer= */
    null, /* progressListener= */
    null);
    cacheWriter.cache();
    // Create blocking CacheDataSource.
    CacheDataSource cacheDataSource = new CacheDataSource(cache, upstream, CacheDataSource.FLAG_BLOCK_ON_CACHE);
    cacheDataSource.open(unboundedDataSpec);
    // Read the first half from upstream as it hasn't cached yet.
    DataSourceUtil.readExactly(cacheDataSource, halfDataLength);
    // Delete the cached latter half.
    NavigableSet<CacheSpan> cachedSpans = cache.getCachedSpans(defaultCacheKey);
    for (CacheSpan cachedSpan : cachedSpans) {
        if (cachedSpan.position >= halfDataLength) {
            cache.removeSpan(cachedSpan);
        }
    }
    // Read the rest of the data.
    DataSourceUtil.readToEnd(cacheDataSource);
    cacheDataSource.close();
}
Also used : FakeDataSource(com.google.android.exoplayer2.testutil.FakeDataSource) DataSpec(com.google.android.exoplayer2.upstream.DataSpec) Test(org.junit.Test)

Example 13 with CacheDataSource

use of com.google.android.exoplayer2.upstream.cache.CacheDataSource in project ExoPlayer by google.

the class CacheDataSourceTest method unknownLengthContentReadInOneConnectionAndLengthIsResolved.

@Test
public void unknownLengthContentReadInOneConnectionAndLengthIsResolved() throws Exception {
    FakeDataSource upstream = new FakeDataSource();
    upstream.getDataSet().newData(testDataUri).appendReadData(TEST_DATA).setSimulateUnknownLength(true);
    CacheDataSource cacheDataSource = new CacheDataSource(cache, upstream, 0);
    cacheDataSource.open(unboundedDataSpec);
    DataSourceUtil.readToEnd(cacheDataSource);
    cacheDataSource.close();
    assertThat(upstream.getAndClearOpenedDataSpecs()).hasLength(1);
    assertThat(ContentMetadata.getContentLength(cache.getContentMetadata(defaultCacheKey))).isEqualTo(TEST_DATA.length);
}
Also used : FakeDataSource(com.google.android.exoplayer2.testutil.FakeDataSource) Test(org.junit.Test)

Example 14 with CacheDataSource

use of com.google.android.exoplayer2.upstream.cache.CacheDataSource in project ExoPlayer by google.

the class CacheAsserts method assertDataCached.

/**
 * Asserts that the cache contains the given data for {@code dataSpec}.
 *
 * @throws IOException If an error occurred reading from the Cache.
 */
public static void assertDataCached(Cache cache, DataSpec dataSpec, byte[] expected) throws IOException {
    DataSource dataSource = new CacheDataSource(cache, DummyDataSource.INSTANCE, 0);
    byte[] bytes;
    try {
        dataSource.open(dataSpec);
        bytes = DataSourceUtil.readToEnd(dataSource);
    } catch (IOException e) {
        throw new IOException("Opening/reading cache failed: " + dataSpec, e);
    } finally {
        dataSource.close();
    }
    assertWithMessage("Cached data doesn't match expected for '" + dataSpec.uri + "',").that(bytes).isEqualTo(expected);
}
Also used : CacheDataSource(com.google.android.exoplayer2.upstream.cache.CacheDataSource) IOException(java.io.IOException) CacheDataSource(com.google.android.exoplayer2.upstream.cache.CacheDataSource) DataSource(com.google.android.exoplayer2.upstream.DataSource) DummyDataSource(com.google.android.exoplayer2.upstream.DummyDataSource)

Example 15 with CacheDataSource

use of com.google.android.exoplayer2.upstream.cache.CacheDataSource in project ExoPlayer by google.

the class SegmentDownloader method download.

@Override
public final void download(@Nullable ProgressListener progressListener) throws IOException, InterruptedException {
    ArrayDeque<Segment> pendingSegments = new ArrayDeque<>();
    ArrayDeque<SegmentDownloadRunnable> recycledRunnables = new ArrayDeque<>();
    if (priorityTaskManager != null) {
        priorityTaskManager.add(C.PRIORITY_DOWNLOAD);
    }
    try {
        CacheDataSource dataSource = cacheDataSourceFactory.createDataSourceForDownloading();
        // Get the manifest and all of the segments.
        M manifest = getManifest(dataSource, manifestDataSpec, /* removing= */
        false);
        if (!streamKeys.isEmpty()) {
            manifest = manifest.copy(streamKeys);
        }
        List<Segment> segments = getSegments(dataSource, manifest, /* removing= */
        false);
        // Sort the segments so that we download media in the right order from the start of the
        // content, and merge segments where possible to minimize the number of server round trips.
        Collections.sort(segments);
        mergeSegments(segments, cacheKeyFactory);
        // Scan the segments, removing any that are fully downloaded.
        int totalSegments = segments.size();
        int segmentsDownloaded = 0;
        long contentLength = 0;
        long bytesDownloaded = 0;
        for (int i = segments.size() - 1; i >= 0; i--) {
            DataSpec dataSpec = segments.get(i).dataSpec;
            String cacheKey = cacheKeyFactory.buildCacheKey(dataSpec);
            long segmentLength = dataSpec.length;
            if (segmentLength == C.LENGTH_UNSET) {
                long resourceLength = ContentMetadata.getContentLength(cache.getContentMetadata(cacheKey));
                if (resourceLength != C.LENGTH_UNSET) {
                    segmentLength = resourceLength - dataSpec.position;
                }
            }
            long segmentBytesDownloaded = cache.getCachedBytes(cacheKey, dataSpec.position, segmentLength);
            bytesDownloaded += segmentBytesDownloaded;
            if (segmentLength != C.LENGTH_UNSET) {
                if (segmentLength == segmentBytesDownloaded) {
                    // The segment is fully downloaded.
                    segmentsDownloaded++;
                    segments.remove(i);
                }
                if (contentLength != C.LENGTH_UNSET) {
                    contentLength += segmentLength;
                }
            } else {
                contentLength = C.LENGTH_UNSET;
            }
        }
        // Download the segments.
        @Nullable ProgressNotifier progressNotifier = progressListener != null ? new ProgressNotifier(progressListener, contentLength, totalSegments, bytesDownloaded, segmentsDownloaded) : null;
        pendingSegments.addAll(segments);
        while (!isCanceled && !pendingSegments.isEmpty()) {
            // Block until there aren't any higher priority tasks.
            if (priorityTaskManager != null) {
                priorityTaskManager.proceed(C.PRIORITY_DOWNLOAD);
            }
            // Create and execute a runnable to download the next segment.
            CacheDataSource segmentDataSource;
            byte[] temporaryBuffer;
            if (!recycledRunnables.isEmpty()) {
                SegmentDownloadRunnable recycledRunnable = recycledRunnables.removeFirst();
                segmentDataSource = recycledRunnable.dataSource;
                temporaryBuffer = recycledRunnable.temporaryBuffer;
            } else {
                segmentDataSource = cacheDataSourceFactory.createDataSourceForDownloading();
                temporaryBuffer = new byte[BUFFER_SIZE_BYTES];
            }
            Segment segment = pendingSegments.removeFirst();
            SegmentDownloadRunnable downloadRunnable = new SegmentDownloadRunnable(segment, segmentDataSource, progressNotifier, temporaryBuffer);
            addActiveRunnable(downloadRunnable);
            executor.execute(downloadRunnable);
            // Clean up runnables that have finished.
            for (int j = activeRunnables.size() - 1; j >= 0; j--) {
                SegmentDownloadRunnable activeRunnable = (SegmentDownloadRunnable) activeRunnables.get(j);
                // it's already finished.
                if (pendingSegments.isEmpty() || activeRunnable.isDone()) {
                    try {
                        activeRunnable.get();
                        removeActiveRunnable(j);
                        recycledRunnables.addLast(activeRunnable);
                    } catch (ExecutionException e) {
                        Throwable cause = Assertions.checkNotNull(e.getCause());
                        if (cause instanceof PriorityTooLowException) {
                            // We need to schedule this segment again in a future loop iteration.
                            pendingSegments.addFirst(activeRunnable.segment);
                            removeActiveRunnable(j);
                            recycledRunnables.addLast(activeRunnable);
                        } else if (cause instanceof IOException) {
                            throw (IOException) cause;
                        } else {
                            // The cause must be an uncaught Throwable type.
                            Util.sneakyThrow(cause);
                        }
                    }
                }
            }
            // Don't move on to the next segment until the runnable for this segment has started. This
            // drip feeds runnables to the executor, rather than providing them all up front.
            downloadRunnable.blockUntilStarted();
        }
    } finally {
        // Cancel them to speed this up.
        for (int i = 0; i < activeRunnables.size(); i++) {
            activeRunnables.get(i).cancel(/* interruptIfRunning= */
            true);
        }
        // do this for the case where the main download thread was interrupted as part of cancelation.
        for (int i = activeRunnables.size() - 1; i >= 0; i--) {
            activeRunnables.get(i).blockUntilFinished();
            removeActiveRunnable(i);
        }
        if (priorityTaskManager != null) {
            priorityTaskManager.remove(C.PRIORITY_DOWNLOAD);
        }
    }
}
Also used : IOException(java.io.IOException) ArrayDeque(java.util.ArrayDeque) PriorityTooLowException(com.google.android.exoplayer2.util.PriorityTaskManager.PriorityTooLowException) CacheDataSource(com.google.android.exoplayer2.upstream.cache.CacheDataSource) DataSpec(com.google.android.exoplayer2.upstream.DataSpec) ExecutionException(java.util.concurrent.ExecutionException) Nullable(androidx.annotation.Nullable)

Aggregations

FakeDataSource (com.google.android.exoplayer2.testutil.FakeDataSource)17 Test (org.junit.Test)15 DataSpec (com.google.android.exoplayer2.upstream.DataSpec)13 FakeDataSet (com.google.android.exoplayer2.testutil.FakeDataSet)7 FileDataSource (com.google.android.exoplayer2.upstream.FileDataSource)7 CacheDataSource (com.google.android.exoplayer2.upstream.cache.CacheDataSource)5 IOException (java.io.IOException)5 Uri (android.net.Uri)3 File (java.io.File)3 FakeData (com.google.android.exoplayer2.testutil.FakeDataSet.FakeData)2 DataSource (com.google.android.exoplayer2.upstream.DataSource)2 CacheDataSink (com.google.android.exoplayer2.upstream.cache.CacheDataSink)2 PriorityTooLowException (com.google.android.exoplayer2.util.PriorityTaskManager.PriorityTooLowException)2 ExecutionException (java.util.concurrent.ExecutionException)2 Nullable (androidx.annotation.Nullable)1 FailOnCloseDataSink (com.google.android.exoplayer2.testutil.FailOnCloseDataSink)1 DataSink (com.google.android.exoplayer2.upstream.DataSink)1 DefaultDataSource (com.google.android.exoplayer2.upstream.DefaultDataSource)1 DummyDataSource (com.google.android.exoplayer2.upstream.DummyDataSource)1 LeastRecentlyUsedCacheEvictor (com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor)1