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 171abddd9f6225298066094e20a6c29061b6af4590Nick Chalkopackage com.android.usbtuner.exoplayer; 181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 191abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.media.MediaDataSource; 201abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.media.MediaExtractor; 211abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.os.ConditionVariable; 2248dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.os.SystemClock; 231abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.Log; 241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 251abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.google.android.exoplayer.MediaFormat; 261abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.google.android.exoplayer.MediaFormatHolder; 2748dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.google.android.exoplayer.MediaFormatUtil; 281abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.google.android.exoplayer.SampleHolder; 2948dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.usbtuner.exoplayer.cache.CacheManager; 3048dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.usbtuner.exoplayer.cache.RecordingSampleBuffer; 3148dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.usbtuner.exoplayer.cache.SimpleSampleBuffer; 3248dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.usbtuner.tvinput.PlaybackCacheListener; 331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 341abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.io.IOException; 3548dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport java.util.ArrayList; 3648dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport java.util.List; 3748dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport java.util.Locale; 3848dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport java.util.concurrent.atomic.AtomicLong; 391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/** 4148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * A class that plays a live stream from a given media extractor using an extractor thread. 421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */ 4348dadb49248271b01997862e1335912a4f2e189fYoungsang Chopublic class PlaySampleExtractor implements SampleExtractor { 4448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private static final String TAG = "PlaySampleExtractor"; 451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Maximum bandwidth of 1080p channel is about 2.2MB/s. 2MB for a sample will suffice. 471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int SAMPLE_BUFFER_SIZE = 1024 * 1024 * 2; 4848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private static final AtomicLong ID_COUNTER = new AtomicLong(0); 491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final MediaDataSource mDataSource; 511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final MediaExtractor mMediaExtractor; 521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final ExtractorThread mExtractorThread; 5348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private final CacheManager.SampleBuffer mSampleBuffer; 5448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private final long mId; 5548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private MediaFormat[] mTrackFormats; 561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private boolean mReleased = false; 581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 5948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public PlaySampleExtractor(MediaDataSource source, CacheManager cacheManager, 6048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho PlaybackCacheListener cacheListener, boolean useCache) { 6148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mId = ID_COUNTER.incrementAndGet(); 621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mDataSource = source; 631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mMediaExtractor = new MediaExtractor(); 641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mExtractorThread = new ExtractorThread(); 6548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho if (useCache) { 6648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mSampleBuffer = new RecordingSampleBuffer(cacheManager, cacheListener, true, 6748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho RecordingSampleBuffer.CACHE_REASON_LIVE_PLAYBACK); 6848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } else { 6948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mSampleBuffer = new SimpleSampleBuffer(cacheListener); 7048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private class ExtractorThread extends Thread { 741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private volatile boolean mQuitRequested = false; 751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public ExtractorThread() { 771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko super("ExtractorThread"); 781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void run() { 821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko SampleHolder sample = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL); 8348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho sample.ensureSpaceForWrite(SAMPLE_BUFFER_SIZE); 841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ConditionVariable conditionVariable = new ConditionVariable(); 851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko while (!mQuitRequested) { 861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko fetchSample(sample, conditionVariable); 871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko cleanUp(); 891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private void fetchSample(SampleHolder sample, ConditionVariable conditionVariable) { 921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int index = mMediaExtractor.getSampleTrackIndex(); 931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (index < 0) { 941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.i(TAG, "EoS"); 951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mQuitRequested = true; 9648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mSampleBuffer.setEos(); 971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return; 981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko sample.data.clear(); 1001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko sample.size = mMediaExtractor.readSampleData(sample.data, 0); 1011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (sample.size < 0 || sample.size > SAMPLE_BUFFER_SIZE) { 1021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Should not happen 1031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.e(TAG, "Invalid sample size: " + sample.size); 1041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mMediaExtractor.advance(); 1051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return; 1061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko sample.data.position(sample.size); 1081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko sample.timeUs = mMediaExtractor.getSampleTime(); 1091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko sample.flags = mMediaExtractor.getSampleFlags(); 1101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mMediaExtractor.advance(); 1121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko try { 1131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko queueSample(index, sample, conditionVariable); 1141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } catch (IOException e) { 1151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mQuitRequested = true; 11648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mSampleBuffer.setEos(); 1171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void quit() { 1211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mQuitRequested = true; 1221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 12548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public void queueSample(int index, SampleHolder sample, ConditionVariable conditionVariable) 12648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho throws IOException { 12748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho long writeStartTimeNs = SystemClock.elapsedRealtimeNanos(); 12848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mSampleBuffer.writeSample(index, sample, conditionVariable); 1291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 13048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho // Check if the storage has enough bandwidth for trickplay. Otherwise we disable it 13148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho // and notify the slowness through the playback cache listener. 13248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho if (mSampleBuffer.isWriteSpeedSlow(sample.size, 13348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho SystemClock.elapsedRealtimeNanos() - writeStartTimeNs)) { 13448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mSampleBuffer.handleWriteSpeedSlow(); 13548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 13648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 1371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public boolean prepare() throws IOException { 1401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko synchronized (this) { 1411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mMediaExtractor.setDataSource(mDataSource); 1421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int trackCount = mMediaExtractor.getTrackCount(); 14448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mTrackFormats = new MediaFormat[trackCount]; 1451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko for (int i = 0; i < trackCount; i++) { 14648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mTrackFormats[i] = 14748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho MediaFormatUtil.createMediaFormat(mMediaExtractor.getTrackFormat(i)); 1481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mMediaExtractor.selectTrack(i); 1491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 15048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho List<String> ids = new ArrayList<>(); 15148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho for (int i = 0; i < trackCount; i++) { 15248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho ids.add(String.format(Locale.ENGLISH, "%s_%x", Long.toHexString(mId), i)); 15348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 15448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 15548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mSampleBuffer.init(ids, null); 15648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 1571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mExtractorThread.start(); 1591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return true; 1601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 16348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public synchronized MediaFormat[] getTrackFormats() { 16448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho return mTrackFormats; 1651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 16748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho @Override 16848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public void getTrackMediaFormat(int track, MediaFormatHolder outMediaFormatHolder) { 16948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho outMediaFormatHolder.format = mTrackFormats[track]; 17048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho outMediaFormatHolder.drmInitData = null; 1711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 17348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho @Override 17448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public void selectTrack(int index) { 17548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mSampleBuffer.selectTrack(index); 1761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 17948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public void deselectTrack(int index) { 18048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mSampleBuffer.deselectTrack(index); 18148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 18248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 18348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho @Override 18448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public long getBufferedPositionUs() { 18548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho return mSampleBuffer.getBufferedPositionUs(); 18648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 18748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 18848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho @Override 18948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public boolean continueBuffering(long positionUs) { 19048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho return mSampleBuffer.continueBuffering(positionUs); 19148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 19248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 19348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho @Override 19448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public void seekTo(long positionUs) { 19548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mSampleBuffer.seekTo(positionUs); 19648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 19748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 19848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho @Override 19948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public int readSample(int track, SampleHolder sampleHolder) { 20048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho return mSampleBuffer.readSample(track, sampleHolder); 2011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 2041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void release() { 2051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko synchronized (this) { 2061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mReleased = true; 2071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mExtractorThread.isAlive()) { 2091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mExtractorThread.quit(); 2101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // We don't join here to prevent hang --- MediaExtractor is released at the thread. 2121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else { 2131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko cleanUp(); 2141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 21748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public void cleanUpImpl() { 21848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mSampleBuffer.release(); 21948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 2201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public synchronized void cleanUp() { 2221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (!mReleased) { 2231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return; 2241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko cleanUpImpl(); 2261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mMediaExtractor.release(); 2271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko} 229