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 */ 16 package com.android.tv.tuner.exoplayer; 17 18 import com.google.android.exoplayer.C; 19 import com.google.android.exoplayer.MediaFormat; 20 import com.google.android.exoplayer.MediaFormatHolder; 21 import com.google.android.exoplayer.SampleHolder; 22 import com.google.android.exoplayer.SampleSource; 23 import com.google.android.exoplayer.SampleSource.SampleSourceReader; 24 import com.google.android.exoplayer.util.Assertions; 25 26 import java.io.IOException; 27 import java.util.ArrayList; 28 import java.util.List; 29 30 /** {@link SampleSource} that extracts sample data using a {@link SampleExtractor}. */ 31 public final class MpegTsSampleSource implements SampleSource, SampleSourceReader { 32 33 private static final int TRACK_STATE_DISABLED = 0; 34 private static final int TRACK_STATE_ENABLED = 1; 35 private static final int TRACK_STATE_FORMAT_SENT = 2; 36 37 private final SampleExtractor mSampleExtractor; 38 private final List<Integer> mTrackStates = new ArrayList<>(); 39 private final List<Boolean> mPendingDiscontinuities = new ArrayList<>(); 40 41 private boolean mPrepared; 42 private IOException mPreparationError; 43 private int mRemainingReleaseCount; 44 45 private long mLastSeekPositionUs; 46 private long mPendingSeekPositionUs; 47 48 /** 49 * Creates a new sample source that extracts samples using {@code mSampleExtractor}. 50 * 51 * @param sampleExtractor a sample extractor for accessing media samples 52 */ MpegTsSampleSource(SampleExtractor sampleExtractor)53 public MpegTsSampleSource(SampleExtractor sampleExtractor) { 54 mSampleExtractor = Assertions.checkNotNull(sampleExtractor); 55 } 56 57 @Override register()58 public SampleSourceReader register() { 59 mRemainingReleaseCount++; 60 return this; 61 } 62 63 @Override prepare(long positionUs)64 public boolean prepare(long positionUs) { 65 if (!mPrepared) { 66 if (mPreparationError != null) { 67 return false; 68 } 69 try { 70 if (mSampleExtractor.prepare()) { 71 int trackCount = mSampleExtractor.getTrackFormats().size(); 72 mTrackStates.clear(); 73 mPendingDiscontinuities.clear(); 74 for (int i = 0; i < trackCount; ++i) { 75 mTrackStates.add(i, TRACK_STATE_DISABLED); 76 mPendingDiscontinuities.add(i, false); 77 } 78 mPrepared = true; 79 } else { 80 return false; 81 } 82 } catch (IOException e) { 83 mPreparationError = e; 84 return false; 85 } 86 } 87 return true; 88 } 89 90 @Override getTrackCount()91 public int getTrackCount() { 92 Assertions.checkState(mPrepared); 93 return mSampleExtractor.getTrackFormats().size(); 94 } 95 96 @Override getFormat(int track)97 public MediaFormat getFormat(int track) { 98 Assertions.checkState(mPrepared); 99 return mSampleExtractor.getTrackFormats().get(track); 100 } 101 102 @Override enable(int track, long positionUs)103 public void enable(int track, long positionUs) { 104 Assertions.checkState(mPrepared); 105 Assertions.checkState(mTrackStates.get(track) == TRACK_STATE_DISABLED); 106 mTrackStates.set(track, TRACK_STATE_ENABLED); 107 mSampleExtractor.selectTrack(track); 108 seekToUsInternal(positionUs, positionUs != 0); 109 } 110 111 @Override disable(int track)112 public void disable(int track) { 113 Assertions.checkState(mPrepared); 114 Assertions.checkState(mTrackStates.get(track) != TRACK_STATE_DISABLED); 115 mSampleExtractor.deselectTrack(track); 116 mPendingDiscontinuities.set(track, false); 117 mTrackStates.set(track, TRACK_STATE_DISABLED); 118 } 119 120 @Override continueBuffering(int track, long positionUs)121 public boolean continueBuffering(int track, long positionUs) { 122 return mSampleExtractor.continueBuffering(positionUs); 123 } 124 125 @Override readDiscontinuity(int track)126 public long readDiscontinuity(int track) { 127 if (mPendingDiscontinuities.get(track)) { 128 mPendingDiscontinuities.set(track, false); 129 return mLastSeekPositionUs; 130 } 131 return NO_DISCONTINUITY; 132 } 133 134 @Override readData(int track, long positionUs, MediaFormatHolder formatHolder, SampleHolder sampleHolder)135 public int readData(int track, long positionUs, MediaFormatHolder formatHolder, 136 SampleHolder sampleHolder) { 137 Assertions.checkState(mPrepared); 138 Assertions.checkState(mTrackStates.get(track) != TRACK_STATE_DISABLED); 139 if (mPendingDiscontinuities.get(track)) { 140 return NOTHING_READ; 141 } 142 if (mTrackStates.get(track) != TRACK_STATE_FORMAT_SENT) { 143 mSampleExtractor.getTrackMediaFormat(track, formatHolder); 144 mTrackStates.set(track, TRACK_STATE_FORMAT_SENT); 145 return FORMAT_READ; 146 } 147 148 mPendingSeekPositionUs = C.UNKNOWN_TIME_US; 149 return mSampleExtractor.readSample(track, sampleHolder); 150 } 151 152 @Override maybeThrowError()153 public void maybeThrowError() throws IOException { 154 if (mPreparationError != null) { 155 throw mPreparationError; 156 } 157 if (mSampleExtractor != null) { 158 mSampleExtractor.maybeThrowError(); 159 } 160 } 161 162 @Override seekToUs(long positionUs)163 public void seekToUs(long positionUs) { 164 Assertions.checkState(mPrepared); 165 seekToUsInternal(positionUs, false); 166 } 167 168 @Override getBufferedPositionUs()169 public long getBufferedPositionUs() { 170 Assertions.checkState(mPrepared); 171 return mSampleExtractor.getBufferedPositionUs(); 172 } 173 174 @Override release()175 public void release() { 176 Assertions.checkState(mRemainingReleaseCount > 0); 177 if (--mRemainingReleaseCount == 0) { 178 mSampleExtractor.release(); 179 } 180 } 181 seekToUsInternal(long positionUs, boolean force)182 private void seekToUsInternal(long positionUs, boolean force) { 183 // Unless forced, avoid duplicate calls to the underlying extractor's seek method 184 // in the case that there have been no interleaving calls to readSample. 185 if (force || mPendingSeekPositionUs != positionUs) { 186 mLastSeekPositionUs = positionUs; 187 mPendingSeekPositionUs = positionUs; 188 mSampleExtractor.seekTo(positionUs); 189 for (int i = 0; i < mTrackStates.size(); ++i) { 190 if (mTrackStates.get(i) != TRACK_STATE_DISABLED) { 191 mPendingDiscontinuities.set(i, true); 192 } 193 } 194 } 195 } 196 } 197