1 /* 2 * Copyright (C) 2019 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 17 package com.android.tv.tuner.exoplayer2; 18 19 import com.google.android.exoplayer2.C; 20 import com.google.android.exoplayer2.FormatHolder; 21 import com.google.android.exoplayer2.SeekParameters; 22 import com.google.android.exoplayer2.decoder.DecoderInputBuffer; 23 import com.google.android.exoplayer2.source.MediaPeriod; 24 import com.google.android.exoplayer2.source.SampleStream; 25 import com.google.android.exoplayer2.source.TrackGroupArray; 26 import com.google.android.exoplayer2.trackselection.TrackSelection; 27 import com.google.android.exoplayer2.util.Assertions; 28 29 import java.io.IOException; 30 import java.util.ArrayList; 31 import java.util.List; 32 33 /** A {@link MediaPeriod} that extracts data using an {@link SampleExtractor}. */ 34 /* package */ final class MpegTsMediaPeriod implements MediaPeriod, SampleExtractor.Callback { 35 36 private static final int TRACK_STATE_DISABLED = 0; 37 private static final int TRACK_STATE_ENABLED = 1; 38 private static final int TRACK_STATE_FORMAT_SENT = 2; 39 40 private final SampleExtractor mExtractor; 41 private final ArrayList<SampleStreamImpl> mSampleStreams = new ArrayList<>(); 42 private final List<Integer> mTrackStates = new ArrayList<>(); 43 private final List<Boolean> mPendingDiscontinuities = new ArrayList<>(); 44 45 private boolean mPrepared; 46 private long mLastSeekPositionUs; 47 private long mPendingSeekPositionUs; 48 private Callback mCallback; 49 private IOException mExceptionOnPrepare; 50 MpegTsMediaPeriod(SampleExtractor extractor)51 public MpegTsMediaPeriod(SampleExtractor extractor) { 52 this.mExtractor = extractor; 53 } 54 55 @Override prepare(Callback callback, long positionUs)56 public void prepare(Callback callback, long positionUs) { 57 mCallback = callback; 58 try { 59 mExtractor.prepare(this); 60 } catch (IOException e) { 61 mExceptionOnPrepare = e; 62 } 63 } 64 enable(int track)65 private void enable(int track) { 66 Assertions.checkState(mPrepared); 67 Assertions.checkState(mTrackStates.get(track) == TRACK_STATE_DISABLED); 68 mTrackStates.set(track, TRACK_STATE_ENABLED); 69 mExtractor.selectTrack(track); 70 } 71 disable(int track)72 private void disable(int track) { 73 Assertions.checkState(mPrepared); 74 Assertions.checkState(mTrackStates.get(track) != TRACK_STATE_DISABLED); 75 mExtractor.deselectTrack(track); 76 mPendingDiscontinuities.set(track, false); 77 mTrackStates.set(track, TRACK_STATE_DISABLED); 78 } 79 80 @Override maybeThrowPrepareError()81 public void maybeThrowPrepareError() throws IOException { 82 if (mExceptionOnPrepare != null) { 83 IOException e = mExceptionOnPrepare; 84 mExceptionOnPrepare = null; 85 throw e; 86 } 87 } 88 89 @Override getTrackGroups()90 public TrackGroupArray getTrackGroups() { 91 return mExtractor.getTrackGroups(); 92 } 93 94 @Override selectTracks( TrackSelection[] selections, boolean[] mayRetainStreamFlags, SampleStream[] streams, boolean[] streamResetFlags, long positionUs)95 public long selectTracks( 96 TrackSelection[] selections, 97 boolean[] mayRetainStreamFlags, 98 SampleStream[] streams, 99 boolean[] streamResetFlags, 100 long positionUs) { 101 TrackGroupArray trackGroups = mExtractor.getTrackGroups(); 102 for (int i = 0; i < selections.length; i++) { 103 if (streams[i] != null && (selections[i] == null || !mayRetainStreamFlags[i])) { 104 SampleStreamImpl stream = (SampleStreamImpl) streams[i]; 105 disable(stream.mIndex); 106 mSampleStreams.remove(stream); 107 streams[i] = null; 108 } 109 if (streams[i] == null && selections[i] != null) { 110 int index = trackGroups.indexOf(selections[i].getTrackGroup()); 111 SampleStreamImpl stream = new SampleStreamImpl(index); 112 mSampleStreams.add(stream); 113 streams[i] = stream; 114 streamResetFlags[i] = true; 115 enable(index); 116 } 117 } 118 seekToUsInternal(positionUs, positionUs != 0); 119 return positionUs; 120 } 121 122 @Override discardBuffer(long positionUs, boolean toKeyframe)123 public void discardBuffer(long positionUs, boolean toKeyframe) { 124 // Handled by extractor 125 } 126 127 @Override reevaluateBuffer(long positionUs)128 public void reevaluateBuffer(long positionUs) { 129 // Do nothing. 130 } 131 132 @Override continueLoading(long positionUs)133 public boolean continueLoading(long positionUs) { 134 return mExtractor.continueLoading(positionUs); 135 } 136 137 @Override readDiscontinuity()138 public long readDiscontinuity() { 139 boolean notifyDiscontinuity = false; 140 for (int i = 0; i < mPendingDiscontinuities.size(); i++) { 141 if (mPendingDiscontinuities.get(i)) { 142 mPendingDiscontinuities.set(i, false); 143 notifyDiscontinuity = true; 144 } 145 } 146 return (notifyDiscontinuity ? mLastSeekPositionUs : C.TIME_UNSET); 147 } 148 149 @Override getNextLoadPositionUs()150 public long getNextLoadPositionUs() { 151 return mExtractor.getNextLoadPositionUs(); 152 } 153 154 @Override getBufferedPositionUs()155 public long getBufferedPositionUs() { 156 return mExtractor.getBufferedPositionUs(); 157 } 158 159 @Override seekToUs(long positionUs)160 public long seekToUs(long positionUs) { 161 for (int i = 0; i < mSampleStreams.size(); i++) { 162 mSampleStreams.get(i).reset(); 163 } 164 seekToUsInternal(positionUs, false); 165 return positionUs; 166 } 167 seekToUsInternal(long positionUs, boolean force)168 private void seekToUsInternal(long positionUs, boolean force) { 169 // Unless forced, avoid duplicate calls to the underlying extractor's seek method 170 // in the case that there have been no interleaving calls to readSample. 171 if (force || mPendingSeekPositionUs != positionUs) { 172 mLastSeekPositionUs = positionUs; 173 mPendingSeekPositionUs = positionUs; 174 mExtractor.seekTo(positionUs); 175 for (int i = 0; i < mTrackStates.size(); ++i) { 176 if (mTrackStates.get(i) != TRACK_STATE_DISABLED) { 177 mPendingDiscontinuities.set(i, true); 178 } 179 } 180 } 181 } 182 183 @Override getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters)184 public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) { 185 return positionUs; 186 } 187 188 @Override onPrepared()189 public void onPrepared() { 190 mPrepared = true; 191 int trackCount = mExtractor.getTrackGroups().length; 192 mTrackStates.clear(); 193 mPendingDiscontinuities.clear(); 194 for (int i = 0; i < trackCount; ++i) { 195 mTrackStates.add(i, TRACK_STATE_DISABLED); 196 mPendingDiscontinuities.add(i, false); 197 } 198 mCallback.onPrepared(this); 199 } 200 release()201 public void release() { 202 mExtractor.release(); 203 } 204 205 private final class SampleStreamImpl implements SampleStream { 206 207 private static final int STREAM_STATE_SEND_FORMAT = 0; 208 private static final int STREAM_STATE_SEND_SAMPLE = 1; 209 private static final int STREAM_STATE_END_OF_STREAM = 2; 210 private final int mIndex; 211 212 private int streamState; 213 SampleStreamImpl(int index)214 SampleStreamImpl(int index) { 215 mIndex = index; 216 } 217 reset()218 void reset() { 219 if (streamState == STREAM_STATE_END_OF_STREAM) { 220 streamState = STREAM_STATE_SEND_SAMPLE; 221 } 222 } 223 224 @Override isReady()225 public boolean isReady() { 226 return true; 227 } 228 229 @Override maybeThrowError()230 public void maybeThrowError() throws IOException { 231 mExtractor.maybeThrowError(); 232 } 233 234 @Override readData( FormatHolder formatHolder, DecoderInputBuffer buffer, boolean requireFormat)235 public int readData( 236 FormatHolder formatHolder, DecoderInputBuffer buffer, boolean requireFormat) { 237 Assertions.checkState(mPrepared); 238 Assertions.checkState(mTrackStates.get(mIndex) != TRACK_STATE_DISABLED); 239 if (mPendingDiscontinuities.get(mIndex)) { 240 return C.RESULT_NOTHING_READ; 241 } 242 if (requireFormat || mTrackStates.get(mIndex) != TRACK_STATE_FORMAT_SENT) { 243 mExtractor.getTrackMediaFormat(mIndex, formatHolder); 244 mTrackStates.set(mIndex, TRACK_STATE_FORMAT_SENT); 245 return C.RESULT_FORMAT_READ; 246 } 247 mPendingSeekPositionUs = C.TIME_UNSET; 248 return mExtractor.readSample(mIndex, buffer); 249 } 250 251 @Override skipData(long positionUs)252 public int skipData(long positionUs) { 253 if (positionUs > 0 && streamState != STREAM_STATE_END_OF_STREAM) { 254 streamState = STREAM_STATE_END_OF_STREAM; 255 return 1; 256 } 257 return 0; 258 } 259 } 260 } 261