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