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