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