blob: 6007b0be5017f19af49b7cd84684b9095e47e3c5 [file] [log] [blame]
/*
* Copyright (C) 2015 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.android.tv.tuner.exoplayer;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SampleSource.SampleSourceReader;
import com.google.android.exoplayer.util.Assertions;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/** {@link SampleSource} that extracts sample data using a {@link SampleExtractor}. */
public final class MpegTsSampleSource implements SampleSource, SampleSourceReader {
private static final int TRACK_STATE_DISABLED = 0;
private static final int TRACK_STATE_ENABLED = 1;
private static final int TRACK_STATE_FORMAT_SENT = 2;
private final SampleExtractor mSampleExtractor;
private final List<Integer> mTrackStates = new ArrayList<>();
private final List<Boolean> mPendingDiscontinuities = new ArrayList<>();
private boolean mPrepared;
private IOException mPreparationError;
private int mRemainingReleaseCount;
private long mLastSeekPositionUs;
private long mPendingSeekPositionUs;
/**
* Creates a new sample source that extracts samples using {@code mSampleExtractor}.
*
* @param sampleExtractor a sample extractor for accessing media samples
*/
public MpegTsSampleSource(SampleExtractor sampleExtractor) {
mSampleExtractor = Assertions.checkNotNull(sampleExtractor);
}
@Override
public SampleSourceReader register() {
mRemainingReleaseCount++;
return this;
}
@Override
public boolean prepare(long positionUs) {
if (!mPrepared) {
if (mPreparationError != null) {
return false;
}
try {
if (mSampleExtractor.prepare()) {
int trackCount = mSampleExtractor.getTrackFormats().size();
mTrackStates.clear();
mPendingDiscontinuities.clear();
for (int i = 0; i < trackCount; ++i) {
mTrackStates.add(i, TRACK_STATE_DISABLED);
mPendingDiscontinuities.add(i, false);
}
mPrepared = true;
} else {
return false;
}
} catch (IOException e) {
mPreparationError = e;
return false;
}
}
return true;
}
@Override
public int getTrackCount() {
Assertions.checkState(mPrepared);
return mSampleExtractor.getTrackFormats().size();
}
@Override
public MediaFormat getFormat(int track) {
Assertions.checkState(mPrepared);
return mSampleExtractor.getTrackFormats().get(track);
}
@Override
public void enable(int track, long positionUs) {
Assertions.checkState(mPrepared);
Assertions.checkState(mTrackStates.get(track) == TRACK_STATE_DISABLED);
mTrackStates.set(track, TRACK_STATE_ENABLED);
mSampleExtractor.selectTrack(track);
seekToUsInternal(positionUs, positionUs != 0);
}
@Override
public void disable(int track) {
Assertions.checkState(mPrepared);
Assertions.checkState(mTrackStates.get(track) != TRACK_STATE_DISABLED);
mSampleExtractor.deselectTrack(track);
mPendingDiscontinuities.set(track, false);
mTrackStates.set(track, TRACK_STATE_DISABLED);
}
@Override
public boolean continueBuffering(int track, long positionUs) {
return mSampleExtractor.continueBuffering(positionUs);
}
@Override
public long readDiscontinuity(int track) {
if (mPendingDiscontinuities.get(track)) {
mPendingDiscontinuities.set(track, false);
return mLastSeekPositionUs;
}
return NO_DISCONTINUITY;
}
@Override
public int readData(int track, long positionUs, MediaFormatHolder formatHolder,
SampleHolder sampleHolder) {
Assertions.checkState(mPrepared);
Assertions.checkState(mTrackStates.get(track) != TRACK_STATE_DISABLED);
if (mPendingDiscontinuities.get(track)) {
return NOTHING_READ;
}
if (mTrackStates.get(track) != TRACK_STATE_FORMAT_SENT) {
mSampleExtractor.getTrackMediaFormat(track, formatHolder);
mTrackStates.set(track, TRACK_STATE_FORMAT_SENT);
return FORMAT_READ;
}
mPendingSeekPositionUs = C.UNKNOWN_TIME_US;
return mSampleExtractor.readSample(track, sampleHolder);
}
@Override
public void maybeThrowError() throws IOException {
if (mPreparationError != null) {
throw mPreparationError;
}
if (mSampleExtractor != null) {
mSampleExtractor.maybeThrowError();
}
}
@Override
public void seekToUs(long positionUs) {
Assertions.checkState(mPrepared);
seekToUsInternal(positionUs, false);
}
@Override
public long getBufferedPositionUs() {
Assertions.checkState(mPrepared);
return mSampleExtractor.getBufferedPositionUs();
}
@Override
public void release() {
Assertions.checkState(mRemainingReleaseCount > 0);
if (--mRemainingReleaseCount == 0) {
mSampleExtractor.release();
}
}
private void seekToUsInternal(long positionUs, boolean force) {
// Unless forced, avoid duplicate calls to the underlying extractor's seek method
// in the case that there have been no interleaving calls to readSample.
if (force || mPendingSeekPositionUs != positionUs) {
mLastSeekPositionUs = positionUs;
mPendingSeekPositionUs = positionUs;
mSampleExtractor.seekTo(positionUs);
for (int i = 0; i < mTrackStates.size(); ++i) {
if (mTrackStates.get(i) != TRACK_STATE_DISABLED) {
mPendingDiscontinuities.set(i, true);
}
}
}
}
}