1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package com.android.usbtuner.exoplayer; 17 18import com.google.android.exoplayer.C; 19import com.google.android.exoplayer.MediaFormat; 20import com.google.android.exoplayer.MediaFormatHolder; 21import com.google.android.exoplayer.SampleHolder; 22import com.google.android.exoplayer.SampleSource; 23import com.google.android.exoplayer.SampleSource.SampleSourceReader; 24import com.google.android.exoplayer.util.Assertions; 25 26import java.io.IOException; 27 28/** {@link SampleSource} that extracts sample data using a {@link SampleExtractor}. */ 29public final class MpegTsSampleSource implements SampleSource, SampleSourceReader { 30 31 private static final int TRACK_STATE_DISABLED = 0; 32 private static final int TRACK_STATE_ENABLED = 1; 33 private static final int TRACK_STATE_FORMAT_SENT = 2; 34 35 private final SampleExtractor mSampleExtractor; 36 37 private MediaFormat[] mTrackFormats; 38 private boolean mPrepared; 39 private IOException mPreparationError; 40 private int mRemainingReleaseCount; 41 private int[] mTrackStates; 42 private boolean[] mPendingDiscontinuities; 43 44 private long mLastSeekPositionUs; 45 private long mPendingSeekPositionUs; 46 47 /** 48 * Creates a new sample source that extracts samples using {@code mSampleExtractor}. 49 * 50 * @param sampleExtractor a sample extractor for accessing media samples 51 */ 52 public MpegTsSampleSource(SampleExtractor sampleExtractor) { 53 mSampleExtractor = Assertions.checkNotNull(sampleExtractor); 54 } 55 56 @Override 57 public SampleSourceReader register() { 58 mRemainingReleaseCount++; 59 return this; 60 } 61 62 @Override 63 public boolean prepare(long positionUs) { 64 if (!mPrepared) { 65 if (mPreparationError != null) { 66 return false; 67 } 68 try { 69 if (mSampleExtractor.prepare()) { 70 mTrackFormats = mSampleExtractor.getTrackFormats(); 71 mTrackStates = new int[mTrackFormats.length]; 72 mPendingDiscontinuities = new boolean[mTrackStates.length]; 73 mPrepared = true; 74 } 75 } catch (IOException e) { 76 mPreparationError = e; 77 return false; 78 } 79 } 80 return true; 81 } 82 83 @Override 84 public int getTrackCount() { 85 Assertions.checkState(mPrepared); 86 return mTrackFormats.length; 87 } 88 89 @Override 90 public MediaFormat getFormat(int track) { 91 Assertions.checkState(mPrepared); 92 return mTrackFormats[track]; 93 } 94 95 @Override 96 public void enable(int track, long positionUs) { 97 Assertions.checkState(mPrepared); 98 Assertions.checkState(mTrackStates[track] == TRACK_STATE_DISABLED); 99 mTrackStates[track] = TRACK_STATE_ENABLED; 100 mSampleExtractor.selectTrack(track); 101 seekToUsInternal(positionUs, positionUs != 0); 102 } 103 104 @Override 105 public void disable(int track) { 106 Assertions.checkState(mPrepared); 107 Assertions.checkState(mTrackStates[track] != TRACK_STATE_DISABLED); 108 mSampleExtractor.deselectTrack(track); 109 mPendingDiscontinuities[track] = false; 110 mTrackStates[track] = TRACK_STATE_DISABLED; 111 } 112 113 @Override 114 public boolean continueBuffering(int track, long positionUs) { 115 return mSampleExtractor.continueBuffering(positionUs); 116 } 117 118 @Override 119 public long readDiscontinuity(int track) { 120 if (mPendingDiscontinuities[track]) { 121 mPendingDiscontinuities[track] = false; 122 return mLastSeekPositionUs; 123 } 124 return NO_DISCONTINUITY; 125 } 126 127 @Override 128 public int readData(int track, long positionUs, MediaFormatHolder formatHolder, 129 SampleHolder sampleHolder) { 130 Assertions.checkState(mPrepared); 131 Assertions.checkState(mTrackStates[track] != TRACK_STATE_DISABLED); 132 if (mPendingDiscontinuities[track]) { 133 return NOTHING_READ; 134 } 135 if (mTrackStates[track] != TRACK_STATE_FORMAT_SENT) { 136 mSampleExtractor.getTrackMediaFormat(track, formatHolder); 137 mTrackStates[track] = TRACK_STATE_FORMAT_SENT; 138 return FORMAT_READ; 139 } 140 141 mPendingSeekPositionUs = C.UNKNOWN_TIME_US; 142 return mSampleExtractor.readSample(track, sampleHolder); 143 } 144 145 @Override 146 public void maybeThrowError() throws IOException { 147 if (mPreparationError != null) { 148 throw mPreparationError; 149 } 150 } 151 152 @Override 153 public void seekToUs(long positionUs) { 154 Assertions.checkState(mPrepared); 155 seekToUsInternal(positionUs, false); 156 } 157 158 @Override 159 public long getBufferedPositionUs() { 160 Assertions.checkState(mPrepared); 161 return mSampleExtractor.getBufferedPositionUs(); 162 } 163 164 @Override 165 public void release() { 166 Assertions.checkState(mRemainingReleaseCount > 0); 167 if (--mRemainingReleaseCount == 0) { 168 mSampleExtractor.release(); 169 } 170 } 171 172 private void seekToUsInternal(long positionUs, boolean force) { 173 // Unless forced, avoid duplicate calls to the underlying extractor's seek method 174 // in the case that there have been no interleaving calls to readSample. 175 if (force || mPendingSeekPositionUs != positionUs) { 176 mLastSeekPositionUs = positionUs; 177 mPendingSeekPositionUs = positionUs; 178 mSampleExtractor.seekTo(positionUs); 179 for (int i = 0; i < mTrackStates.length; ++i) { 180 if (mTrackStates[i] != TRACK_STATE_DISABLED) { 181 mPendingDiscontinuities[i] = true; 182 } 183 } 184 } 185 } 186} 187