/* * Copyright (C) 2014 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.exoplayer.chunk; import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.upstream.NonBlockingInputStream; import com.google.android.exoplayer.util.Assertions; import java.nio.ByteBuffer; import java.util.Map; import java.util.UUID; /** * A {@link MediaChunk} containing a single sample. */ public class SingleSampleMediaChunk extends MediaChunk { /** * The sample header data. May be null. */ public final byte[] headerData; private final MediaFormat sampleFormat; /** * @param dataSource A {@link DataSource} for loading the data. * @param dataSpec Defines the data to be loaded. * @param format The format of the stream to which this chunk belongs. * @param trigger The reason for this chunk being selected. * @param startTimeUs The start time of the media contained by the chunk, in microseconds. * @param endTimeUs The end time of the media contained by the chunk, in microseconds. * @param nextChunkIndex The index of the next chunk, or -1 if this is the last chunk. * @param sampleFormat The format of the media contained by the chunk. */ public SingleSampleMediaChunk(DataSource dataSource, DataSpec dataSpec, Format format, int trigger, long startTimeUs, long endTimeUs, int nextChunkIndex, MediaFormat sampleFormat) { this(dataSource, dataSpec, format, trigger, startTimeUs, endTimeUs, nextChunkIndex, sampleFormat, null); } /** * @param dataSource A {@link DataSource} for loading the data. * @param dataSpec Defines the data to be loaded. * @param format The format of the stream to which this chunk belongs. * @param trigger The reason for this chunk being selected. * @param startTimeUs The start time of the media contained by the chunk, in microseconds. * @param endTimeUs The end time of the media contained by the chunk, in microseconds. * @param nextChunkIndex The index of the next chunk, or -1 if this is the last chunk. * @param sampleFormat The format of the media contained by the chunk. * @param headerData Custom header data for the sample. May be null. If set, the header data is * prepended to the sample data returned when {@link #read(SampleHolder)} is called. It is * however not considered part of the loaded data, and so is not prepended to the data * returned by {@link #getLoadedData()}. It is also not reflected in the values returned by * {@link #bytesLoaded()} and {@link #getLength()}. */ public SingleSampleMediaChunk(DataSource dataSource, DataSpec dataSpec, Format format, int trigger, long startTimeUs, long endTimeUs, int nextChunkIndex, MediaFormat sampleFormat, byte[] headerData) { super(dataSource, dataSpec, format, trigger, startTimeUs, endTimeUs, nextChunkIndex); this.sampleFormat = sampleFormat; this.headerData = headerData; } @Override public boolean prepare() { return true; } @Override public boolean sampleAvailable() { return isLoadFinished() && !isReadFinished(); } @Override public boolean read(SampleHolder holder) { NonBlockingInputStream inputStream = getNonBlockingInputStream(); Assertions.checkState(inputStream != null); if (!sampleAvailable()) { return false; } int bytesLoaded = (int) bytesLoaded(); int sampleSize = bytesLoaded; if (headerData != null) { sampleSize += headerData.length; } if (holder.allowDataBufferReplacement && (holder.data == null || holder.data.capacity() < sampleSize)) { holder.data = ByteBuffer.allocate(sampleSize); } int bytesRead; if (holder.data != null) { if (headerData != null) { holder.data.put(headerData); } bytesRead = inputStream.read(holder.data, bytesLoaded); holder.size = sampleSize; } else { bytesRead = inputStream.skip(bytesLoaded); holder.size = 0; } Assertions.checkState(bytesRead == bytesLoaded); holder.timeUs = startTimeUs; return true; } @Override public void seekToStart() { resetReadPosition(); } @Override public boolean seekTo(long positionUs, boolean allowNoop) { resetReadPosition(); return true; } @Override public MediaFormat getMediaFormat() { return sampleFormat; } @Override public Map<UUID, byte[]> getPsshInfo() { return null; } }