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