• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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