11abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/*
21abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Copyright (C) 2015 The Android Open Source Project
31abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
41abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License");
51abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * you may not use this file except in compliance with the License.
61abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * You may obtain a copy of the License at
71abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
81abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *      http://www.apache.org/licenses/LICENSE-2.0
91abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Unless required by applicable law or agreed to in writing, software
111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS,
121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * See the License for the specific language governing permissions and
141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * limitations under the License.
151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkopackage com.android.tv.tuner.exoplayer;
181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
19d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.net.Uri;
2065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.os.Handler;
211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
222e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport com.google.android.exoplayer.MediaFormat;
231abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.google.android.exoplayer.MediaFormatHolder;
241abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.google.android.exoplayer.SampleHolder;
251abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.google.android.exoplayer.SampleSource;
2665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.google.android.exoplayer.upstream.DataSource;
271abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.google.android.exoplayer.util.MimeTypes;
2865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.exoplayer.buffer.BufferManager;
2965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.exoplayer.buffer.SamplePool;
3065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.tvinput.PlaybackBufferListener;
311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
321abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.io.IOException;
331abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.nio.ByteBuffer;
3465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.ArrayList;
3565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.LinkedList;
3665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.List;
371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/**
39d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * Extracts samples from {@link DataSource} for MPEG-TS streams.
401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
4165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkopublic final class MpegTsSampleExtractor implements SampleExtractor {
421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public static final String MIMETYPE_TEXT_CEA_708 = "text/cea-708";
431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
44ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private static final int CC_BUFFER_SIZE_IN_BYTES = 9600 / 8;
45ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private final SampleExtractor mSampleExtractor;
4765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private final List<MediaFormat> mTrackFormats = new ArrayList<>();
4865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private final List<Boolean> mReachedEos = new ArrayList<>();
491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private int mVideoTrackIndex;
5065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private final SamplePool mCcSamplePool = new SamplePool();
5165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private final List<SampleHolder> mPendingCcSamples = new LinkedList<>();
5265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private int mCea708TextTrackIndex;
541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private boolean mCea708TextTrackSelected;
551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private CcParser mCcParser;
571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
58ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private void init() {
59ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mVideoTrackIndex = -1;
60ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mCea708TextTrackIndex = -1;
61ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mCea708TextTrackSelected = false;
62ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
63ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
642e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    /**
65d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     * Creates MpegTsSampleExtractor for {@link DataSource}.
662e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko     *
67d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     * @param source the {@link DataSource} to extract from
6865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * @param bufferManager the manager for reading & writing samples backed by physical storage
6965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * @param bufferListener the {@link PlaybackBufferListener}
7065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     *                      to notify buffer storage status change
712e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko     */
72d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    public MpegTsSampleExtractor(DataSource source, BufferManager bufferManager,
73d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            PlaybackBufferListener bufferListener) {
74d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        mSampleExtractor = new ExoPlayerSampleExtractor(Uri.EMPTY, source, bufferManager,
75d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                bufferListener, false);
76ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        init();
77ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
78ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
792e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    /**
8065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Creates MpegTsSampleExtractor for a recorded program.
812e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko     *
8265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * @param bufferManager the samples provider which is stored in physical storage
8365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * @param bufferListener the {@link PlaybackBufferListener}
8465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     *                      to notify buffer storage status change
852e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko     */
8665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public MpegTsSampleExtractor(BufferManager bufferManager,
8765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            PlaybackBufferListener bufferListener) {
8865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        mSampleExtractor = new FileSampleExtractor(bufferManager, bufferListener);
89ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        init();
901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
93d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    public void maybeThrowError() throws IOException {
94d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        if (mSampleExtractor != null) {
95d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            mSampleExtractor.maybeThrowError();
96d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        }
97d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    }
98d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
99d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    @Override
1001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public boolean prepare() throws IOException {
1012e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        if(!mSampleExtractor.prepare()) {
1022e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            return false;
1032e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        }
10465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        List<MediaFormat> formats = mSampleExtractor.getTrackFormats();
10565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        int trackCount = formats.size();
10665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        mTrackFormats.clear();
10765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        mReachedEos.clear();
1081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < trackCount; ++i) {
11065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            mTrackFormats.add(formats.get(i));
11165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            mReachedEos.add(false);
11265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            String mime = formats.get(i).mimeType;
1131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (MimeTypes.isVideo(mime) && mVideoTrackIndex == -1) {
1141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                mVideoTrackIndex = i;
1151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (android.media.MediaFormat.MIMETYPE_VIDEO_MPEG2.equals(mime)) {
1161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    mCcParser = new Mpeg2CcParser();
1171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                } else if (android.media.MediaFormat.MIMETYPE_VIDEO_AVC.equals(mime)) {
1181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    mCcParser = new H264CcParser();
1191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
1201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mVideoTrackIndex != -1) {
1241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mCea708TextTrackIndex = trackCount;
1251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mCea708TextTrackIndex >= 0) {
127d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            mTrackFormats.add(MediaFormat.createTextFormat(null, MIMETYPE_TEXT_CEA_708, 0,
128d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    mTrackFormats.get(0).durationUs, ""));
1291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return true;
1311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
13465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public List<MediaFormat> getTrackFormats() {
1352e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        return mTrackFormats;
1361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
1391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public void selectTrack(int index) {
1401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (index == mCea708TextTrackIndex) {
1411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mCea708TextTrackSelected = true;
1421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return;
1431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mSampleExtractor.selectTrack(index);
1451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
1481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public void deselectTrack(int index) {
1491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (index == mCea708TextTrackIndex) {
1501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mCea708TextTrackSelected = false;
1511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return;
1521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mSampleExtractor.deselectTrack(index);
1541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
1571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public long getBufferedPositionUs() {
1581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return mSampleExtractor.getBufferedPositionUs();
1591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
1621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public void seekTo(long positionUs) {
1631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mSampleExtractor.seekTo(positionUs);
16465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        for (SampleHolder holder : mPendingCcSamples) {
16565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            mCcSamplePool.releaseSample(holder);
16665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
16765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        mPendingCcSamples.clear();
1681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
1712e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    public void getTrackMediaFormat(int track, MediaFormatHolder outMediaFormatHolder) {
1721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (track != mCea708TextTrackIndex) {
1732e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            mSampleExtractor.getTrackMediaFormat(track, outMediaFormatHolder);
1741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
1781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public int readSample(int track, SampleHolder sampleHolder) {
1791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (track == mCea708TextTrackIndex) {
18065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (mCea708TextTrackSelected && !mPendingCcSamples.isEmpty()) {
18165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                SampleHolder holder = mPendingCcSamples.remove(0);
18265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                holder.data.flip();
18365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                sampleHolder.timeUs = holder.timeUs;
18465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                sampleHolder.data.put(holder.data);
18565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                mCcSamplePool.releaseSample(holder);
1861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return SampleSource.SAMPLE_READ;
1871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            } else {
18865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                return mVideoTrackIndex < 0 || mReachedEos.get(mVideoTrackIndex)
1891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        ? SampleSource.END_OF_STREAM : SampleSource.NOTHING_READ;
1901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1932e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        int result = mSampleExtractor.readSample(track, sampleHolder);
1941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        switch (result) {
1951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case SampleSource.END_OF_STREAM: {
19665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                mReachedEos.set(track, true);
1971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
1981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case SampleSource.SAMPLE_READ: {
2001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (mCea708TextTrackSelected && track == mVideoTrackIndex
2011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        && sampleHolder.data != null) {
2021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    mCcParser.mayParseClosedCaption(sampleHolder.data, sampleHolder.timeUs);
2031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
2041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
2051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
2061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return result;
2081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
2111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public void release() {
2121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mSampleExtractor.release();
2131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mVideoTrackIndex = -1;
2141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mCea708TextTrackIndex = -1;
2151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mCea708TextTrackSelected = false;
2161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
2191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public boolean continueBuffering(long positionUs) {
2201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return mSampleExtractor.continueBuffering(positionUs);
2211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
22365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    @Override
22465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public void setOnCompletionListener(OnCompletionListener listener, Handler handler) { }
22565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
2261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private abstract class CcParser {
227d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        // Interim buffer for reduce direct access to ByteBuffer which is expensive. Using
228d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        // relatively small buffer size in order to minimize memory footprint increase.
229d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        protected final byte[] mBuffer = new byte[1024];
230d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
2311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        abstract void mayParseClosedCaption(ByteBuffer buffer, long presentationTimeUs);
2321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
233d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        protected int parseClosedCaption(ByteBuffer buffer, int offset, long presentationTimeUs) {
2341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // For the details of user_data_type_structure, see ATSC A/53 Part 4 - Table 6.9.
2351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int pos = offset;
2361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (pos + 2 >= buffer.position()) {
237d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                return offset;
2381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
2391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean processCcDataFlag = (buffer.get(pos) & 64) != 0;
2401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int ccCount = buffer.get(pos) & 0x1f;
2411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += 2;
2421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (!processCcDataFlag || pos + 3 * ccCount >= buffer.position() || ccCount == 0) {
243d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                return offset;
2441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
24565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            SampleHolder holder = mCcSamplePool.acquireSample(CC_BUFFER_SIZE_IN_BYTES);
2461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            for (int i = 0; i < 3 * ccCount; i++) {
247d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                holder.data.put(buffer.get(pos++));
2481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
24965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            holder.timeUs = presentationTimeUs;
25065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            mPendingCcSamples.add(holder);
251d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            return pos;
2521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private class Mpeg2CcParser extends CcParser {
256d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        private static final int PATTERN_LENGTH = 9;
257d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
2581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
2591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public void mayParseClosedCaption(ByteBuffer buffer, long presentationTimeUs) {
260d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            int totalSize = buffer.position();
261d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            // Reading the frame in bulk to reduce the overhead from ByteBuffer.get() with
262d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            // overlapping to handle the case that the pattern exists in the boundary.
263d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            for (int i = 0; i < totalSize; i += mBuffer.length - PATTERN_LENGTH) {
264d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                buffer.position(i);
265d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                int size = Math.min(totalSize - i, mBuffer.length);
266d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                buffer.get(mBuffer, 0, size);
267d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                int j = 0;
268d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                while (j < size - PATTERN_LENGTH) {
269d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    // Find the start prefix code of private user data.
270d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    if (mBuffer[j] == 0
271d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            && mBuffer[j + 1] == 0
272d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            && mBuffer[j + 2] == 1
273d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            && (mBuffer[j + 3] & 0xff) == 0xb2) {
274d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        // ATSC closed caption data embedded in MPEG2VIDEO stream has 'GA94' user
275d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        // identifier and user data type code 3.
276d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        if (mBuffer[j + 4] == 'G'
277d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                                && mBuffer[j + 5] == 'A'
278d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                                && mBuffer[j + 6] == '9'
279d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                                && mBuffer[j + 7] == '4'
280d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                                && mBuffer[j + 8] == 3) {
281d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            j = parseClosedCaption(buffer, i + j + PATTERN_LENGTH,
282d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                                    presentationTimeUs) - i;
283d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        } else {
284d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            j += PATTERN_LENGTH;
285d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        }
286d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    } else {
287d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        ++j;
2881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    }
2891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
2901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
291d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            buffer.position(totalSize);
2921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private class H264CcParser extends CcParser {
296d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        private static final int PATTERN_LENGTH = 14;
297d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
2981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
2991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public void mayParseClosedCaption(ByteBuffer buffer, long presentationTimeUs) {
300d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            int totalSize = buffer.position();
301d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            // Reading the frame in bulk to reduce the overhead from ByteBuffer.get() with
302d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            // overlapping to handle the case that the pattern exists in the boundary.
303d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            for (int i = 0; i < totalSize; i += mBuffer.length - PATTERN_LENGTH) {
304d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                buffer.position(i);
305d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                int size = Math.min(totalSize - i, mBuffer.length);
306d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                buffer.get(mBuffer, 0, size);
307d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                int j = 0;
308d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                while (j < size - PATTERN_LENGTH) {
309d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    // Find the start prefix code of a NAL Unit.
310d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    if (mBuffer[j] == 0
311d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            && mBuffer[j + 1] == 0
312d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            && mBuffer[j + 2] == 1) {
313d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        int nalType = mBuffer[j + 3] & 0x1f;
314d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        int payloadType = mBuffer[j + 4] & 0xff;
315d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
316d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        // ATSC closed caption data embedded in H264 private user data has NAL type
317d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        // 6, payload type 4, and 'GA94' user identifier for ATSC.
318d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        if (nalType == 6 && payloadType == 4 && mBuffer[j + 9] == 'G'
319d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                                && mBuffer[j + 10] == 'A'
320d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                                && mBuffer[j + 11] == '9'
321d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                                && mBuffer[j + 12] == '4') {
322d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            j = parseClosedCaption(buffer, i + j + PATTERN_LENGTH,
323d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                                    presentationTimeUs) - i;
324d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        } else {
325d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            j += 7;
326d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        }
327d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    } else {
328d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        ++j;
3291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    }
3301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
3311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
332d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            buffer.position(totalSize);
3331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
3351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko}
336