use of com.google.android.exoplayer2.upstream.DataSpec in project ExoPlayer by google.
the class CronetDataSourceTest method testOverread.
@Test
public void testOverread() throws HttpDataSourceException {
testDataSpec = new DataSpec(Uri.parse(TEST_URL), 0, 16, null);
testResponseHeader.put("Content-Length", Long.toString(16L));
mockResponseStartSuccess();
mockReadSuccess(0, 16);
dataSourceUnderTest.open(testDataSpec);
byte[] returnedBuffer = new byte[8];
int bytesRead = dataSourceUnderTest.read(returnedBuffer, 0, 8);
assertEquals(8, bytesRead);
assertArrayEquals(buildTestDataArray(0, 8), returnedBuffer);
// The current buffer is kept if not completely consumed by DataSource reader.
returnedBuffer = new byte[8];
bytesRead += dataSourceUnderTest.read(returnedBuffer, 0, 6);
assertEquals(14, bytesRead);
assertArrayEquals(suffixZeros(buildTestDataArray(8, 6), 8), returnedBuffer);
// 2 bytes left at this point.
returnedBuffer = new byte[8];
bytesRead += dataSourceUnderTest.read(returnedBuffer, 0, 8);
assertEquals(16, bytesRead);
assertArrayEquals(suffixZeros(buildTestDataArray(14, 2), 8), returnedBuffer);
// Should have only called read on cronet once.
verify(mockUrlRequest, times(1)).read(any(ByteBuffer.class));
verify(mockTransferListener, times(1)).onBytesTransferred(dataSourceUnderTest, 8);
verify(mockTransferListener, times(1)).onBytesTransferred(dataSourceUnderTest, 6);
verify(mockTransferListener, times(1)).onBytesTransferred(dataSourceUnderTest, 2);
// Now we already returned the 16 bytes initially asked.
// Try to read again even though all requested 16 bytes are already returned.
// Return C.RESULT_END_OF_INPUT
returnedBuffer = new byte[16];
int bytesOverRead = dataSourceUnderTest.read(returnedBuffer, 0, 16);
assertEquals(C.RESULT_END_OF_INPUT, bytesOverRead);
assertArrayEquals(new byte[16], returnedBuffer);
// C.RESULT_END_OF_INPUT should not be reported though the TransferListener.
verify(mockTransferListener, never()).onBytesTransferred(dataSourceUnderTest, C.RESULT_END_OF_INPUT);
// There should still be only one call to read on cronet.
verify(mockUrlRequest, times(1)).read(any(ByteBuffer.class));
// Check for connection not automatically closed.
verify(mockUrlRequest, never()).cancel();
assertEquals(16, bytesRead);
}
use of com.google.android.exoplayer2.upstream.DataSpec in project ExoPlayer by google.
the class CronetDataSourceTest method testRequestHeadersSet.
@Test
public void testRequestHeadersSet() throws HttpDataSourceException {
testDataSpec = new DataSpec(Uri.parse(TEST_URL), 1000, 5000, null);
mockResponseStartSuccess();
dataSourceUnderTest.setRequestProperty("firstHeader", "firstValue");
dataSourceUnderTest.setRequestProperty("secondHeader", "secondValue");
dataSourceUnderTest.open(testDataSpec);
// The header value to add is current position to current position + length - 1.
verify(mockUrlRequestBuilder).addHeader("Range", "bytes=1000-5999");
verify(mockUrlRequestBuilder).addHeader("firstHeader", "firstValue");
verify(mockUrlRequestBuilder).addHeader("secondHeader", "secondValue");
verify(mockUrlRequest).start();
}
use of com.google.android.exoplayer2.upstream.DataSpec in project ExoPlayer by google.
the class DefaultExtractorInputTest method testSkipFullyLarge.
public void testSkipFullyLarge() throws Exception {
// Tests skipping an amount of data that's larger than any internal scratch space.
int largeSkipSize = 1024 * 1024;
FakeDataSource.Builder builder = new FakeDataSource.Builder();
builder.appendReadData(new byte[largeSkipSize]);
FakeDataSource testDataSource = builder.build();
testDataSource.open(new DataSpec(Uri.parse(TEST_URI)));
DefaultExtractorInput input = new DefaultExtractorInput(testDataSource, 0, C.LENGTH_UNSET);
input.skipFully(largeSkipSize);
assertEquals(largeSkipSize, input.getPosition());
// Check that we fail with EOFException we skip again.
try {
input.skipFully(1);
fail();
} catch (EOFException e) {
// Expected.
}
}
use of com.google.android.exoplayer2.upstream.DataSpec in project ExoPlayer by google.
the class CacheDataSourceTest method testUnsatisfiableRange.
public void testUnsatisfiableRange() throws Exception {
// Bounded request but the content length is unknown. This forces all data to be cached but not
// the length
assertCacheAndRead(false, true);
// Now do an unbounded request. This will read all of the data from cache and then try to read
// more from upstream which will cause to a 416 so CDS will store the length.
CacheDataSource cacheDataSource = createCacheDataSource(true, true);
assertReadDataContentLength(cacheDataSource, true, true);
// If the user try to access off range then it should throw an IOException
try {
cacheDataSource = createCacheDataSource(false, false);
cacheDataSource.open(new DataSpec(Uri.EMPTY, TEST_DATA.length, 5, KEY_1));
fail();
} catch (IOException e) {
// success
}
}
use of com.google.android.exoplayer2.upstream.DataSpec in project ExoPlayer by google.
the class HlsChunkSource method getNextChunk.
/**
* Returns the next chunk to load.
* <p>
* If a chunk is available then {@link HlsChunkHolder#chunk} is set. If the end of the stream has
* been reached then {@link HlsChunkHolder#endOfStream} is set. If a chunk is not available but
* the end of the stream has not been reached, {@link HlsChunkHolder#playlist} is set to
* contain the {@link HlsUrl} that refers to the playlist that needs refreshing.
*
* @param previous The most recently loaded media chunk.
* @param playbackPositionUs The current playback position. If {@code previous} is null then this
* parameter is the position from which playback is expected to start (or restart) and hence
* should be interpreted as a seek position.
* @param out A holder to populate.
*/
public void getNextChunk(HlsMediaChunk previous, long playbackPositionUs, HlsChunkHolder out) {
int oldVariantIndex = previous == null ? C.INDEX_UNSET : trackGroup.indexOf(previous.trackFormat);
// Use start time of the previous chunk rather than its end time because switching format will
// require downloading overlapping segments.
long bufferedDurationUs = previous == null ? 0 : Math.max(0, previous.startTimeUs - playbackPositionUs);
// Select the variant.
trackSelection.updateSelectedTrack(bufferedDurationUs);
int selectedVariantIndex = trackSelection.getSelectedIndexInTrackGroup();
boolean switchingVariant = oldVariantIndex != selectedVariantIndex;
HlsUrl selectedUrl = variants[selectedVariantIndex];
if (!playlistTracker.isSnapshotValid(selectedUrl)) {
out.playlist = selectedUrl;
// Retry when playlist is refreshed.
return;
}
HlsMediaPlaylist mediaPlaylist = playlistTracker.getPlaylistSnapshot(selectedUrl);
// Select the chunk.
int chunkMediaSequence;
if (previous == null || switchingVariant) {
long targetPositionUs = previous == null ? playbackPositionUs : previous.startTimeUs;
if (!mediaPlaylist.hasEndTag && targetPositionUs > mediaPlaylist.getEndTimeUs()) {
// If the playlist is too old to contain the chunk, we need to refresh it.
chunkMediaSequence = mediaPlaylist.mediaSequence + mediaPlaylist.segments.size();
} else {
chunkMediaSequence = Util.binarySearchFloor(mediaPlaylist.segments, targetPositionUs - mediaPlaylist.startTimeUs, true, !playlistTracker.isLive() || previous == null) + mediaPlaylist.mediaSequence;
if (chunkMediaSequence < mediaPlaylist.mediaSequence && previous != null) {
// We try getting the next chunk without adapting in case that's the reason for falling
// behind the live window.
selectedVariantIndex = oldVariantIndex;
selectedUrl = variants[selectedVariantIndex];
mediaPlaylist = playlistTracker.getPlaylistSnapshot(selectedUrl);
chunkMediaSequence = previous.getNextChunkIndex();
}
}
} else {
chunkMediaSequence = previous.getNextChunkIndex();
}
if (chunkMediaSequence < mediaPlaylist.mediaSequence) {
fatalError = new BehindLiveWindowException();
return;
}
int chunkIndex = chunkMediaSequence - mediaPlaylist.mediaSequence;
if (chunkIndex >= mediaPlaylist.segments.size()) {
if (mediaPlaylist.hasEndTag) {
out.endOfStream = true;
} else /* Live */
{
out.playlist = selectedUrl;
}
return;
}
// Handle encryption.
HlsMediaPlaylist.Segment segment = mediaPlaylist.segments.get(chunkIndex);
// Check if encryption is specified.
if (segment.isEncrypted) {
Uri keyUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, segment.encryptionKeyUri);
if (!keyUri.equals(encryptionKeyUri)) {
// Encryption is specified and the key has changed.
out.chunk = newEncryptionKeyChunk(keyUri, segment.encryptionIV, selectedVariantIndex, trackSelection.getSelectionReason(), trackSelection.getSelectionData());
return;
}
if (!Util.areEqual(segment.encryptionIV, encryptionIvString)) {
setEncryptionData(keyUri, segment.encryptionIV, encryptionKey);
}
} else {
clearEncryptionData();
}
DataSpec initDataSpec = null;
Segment initSegment = mediaPlaylist.initializationSegment;
if (initSegment != null) {
Uri initSegmentUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, initSegment.url);
initDataSpec = new DataSpec(initSegmentUri, initSegment.byterangeOffset, initSegment.byterangeLength, null);
}
// Compute start time of the next chunk.
long startTimeUs = mediaPlaylist.startTimeUs + segment.relativeStartTimeUs;
int discontinuitySequence = mediaPlaylist.discontinuitySequence + segment.relativeDiscontinuitySequence;
TimestampAdjuster timestampAdjuster = timestampAdjusterProvider.getAdjuster(discontinuitySequence);
// Configure the data source and spec for the chunk.
Uri chunkUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, segment.url);
DataSpec dataSpec = new DataSpec(chunkUri, segment.byterangeOffset, segment.byterangeLength, null);
out.chunk = new HlsMediaChunk(mediaDataSource, dataSpec, initDataSpec, selectedUrl, muxedCaptionFormats, trackSelection.getSelectionReason(), trackSelection.getSelectionData(), startTimeUs, startTimeUs + segment.durationUs, chunkMediaSequence, discontinuitySequence, isTimestampMaster, timestampAdjuster, previous, encryptionKey, encryptionIv);
}
Aggregations