148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho/*
248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * Copyright (C) 2016 The Android Open Source Project
348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho *
448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * Licensed under the Apache License, Version 2.0 (the "License");
548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * you may not use this file except in compliance with the License.
648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * You may obtain a copy of the License at
748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho *
848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho *      http://www.apache.org/licenses/LICENSE-2.0
948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho *
1048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * Unless required by applicable law or agreed to in writing, software
1148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * distributed under the License is distributed on an "AS IS" BASIS,
1248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * See the License for the specific language governing permissions and
1448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * limitations under the License.
1548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho */
1648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
1748dadb49248271b01997862e1335912a4f2e189fYoungsang Chopackage com.android.usbtuner.exoplayer.cache;
1848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
1948dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.media.MediaCodec;
2048dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.media.MediaFormat;
2148dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.os.ConditionVariable;
2248dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.support.annotation.IntDef;
2348dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.util.Log;
2448dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.util.Pair;
2548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
2648dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.google.android.exoplayer.C;
2748dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.google.android.exoplayer.SampleHolder;
2848dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.google.android.exoplayer.SampleSource;
2948dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.google.android.exoplayer.util.MimeTypes;
3048dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.usbtuner.tvinput.PlaybackCacheListener;
3148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
3248dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport java.io.IOException;
3348dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport java.lang.annotation.Retention;
3448dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport java.lang.annotation.RetentionPolicy;
3548dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport java.util.List;
3648dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport java.util.concurrent.TimeUnit;
3748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
3848dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport junit.framework.Assert;
3948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
4048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho/**
4148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * Handles I/O between {@link com.android.usbtuner.exoplayer.SampleExtractor} and
4248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * {@link CacheManager}.Reads & writes samples from/to {@link SampleCache} which is backed
4348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * by physical storage.
4448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho */
4548dadb49248271b01997862e1335912a4f2e189fYoungsang Chopublic class RecordingSampleBuffer implements CacheManager.SampleBuffer,
4648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        CacheManager.EvictListener {
4748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private static final String TAG = "RecordingSampleBuffer";
4848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private static final boolean DEBUG = false;
4948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
5048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @IntDef({CACHE_REASON_LIVE_PLAYBACK, CACHE_REASON_RECORDED_PLAYBACK, CACHE_REASON_RECORDING})
5148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Retention(RetentionPolicy.SOURCE)
5248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public @interface CacheReason {}
5348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
5448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    /**
5548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     * A cache reason for live-stream playback.
5648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     */
5748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public static final int CACHE_REASON_LIVE_PLAYBACK = 0;
5848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
5948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    /**
6048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     * A cache reason for playback of a recorded program.
6148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     */
6248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public static final int CACHE_REASON_RECORDED_PLAYBACK = 1;
6348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
6448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    /**
6548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     * A cache reason for recording a program.
6648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     */
6748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public static final int CACHE_REASON_RECORDING = 2;
6848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
6948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private static final long CACHE_WRITE_TIMEOUT_MS = 10 * 1000;  // 10 seconds
7048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private static final long CHUNK_DURATION_US = TimeUnit.MILLISECONDS.toMicros(500);
7148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private static final long LIVE_THRESHOLD_US = TimeUnit.SECONDS.toMicros(1);
7248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
7348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private final CacheManager mCacheManager;
7448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private final PlaybackCacheListener mCacheListener;
7548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private final int mCacheReason;
7648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
7748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private int mTrackCount;
7848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private List<String> mIds;
7948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private List<MediaFormat> mMediaFormats;
8048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private volatile long mCacheDurationUs = 0;
8148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private long[] mCacheEndPositionUs;
8248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    // SampleCache to append the latest live sample.
8348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private SampleCache[] mSampleCaches;
8448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private CachedSampleQueue[] mPlayingSampleQueues;
8548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private final SamplePool mSamplePool = new SamplePool();
8648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private long mLastBufferedPositionUs = C.UNKNOWN_TIME_US;
8748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private long mCurrentPlaybackPositionUs = 0;
8848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private boolean mEos = false;
8948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
9048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private class CachedSampleQueue extends SampleQueue {
9148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        private SampleCache mCache = null;
9248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
9348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        public CachedSampleQueue(SamplePool samplePool) {
9448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            super(samplePool);
9548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
9648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
9748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        public void setSource(SampleCache newCache) {
9848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            for (SampleCache cache = mCache; cache != null; cache = cache.getNext()) {
9948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                cache.clear();
10048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                cache.close();
10148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
10248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mCache = newCache;
10348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            for (SampleCache cache = mCache; cache != null; cache = cache.getNext()) {
10448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                cache.resetRead();
10548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
10648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
10748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
10848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        public boolean maybeReadSample() {
10948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if (isDurationGreaterThan(CHUNK_DURATION_US)) {
11048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                return false;
11148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
11248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            SampleHolder sample = mCache.maybeReadSample();
11348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if (sample == null) {
11448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                if (!mCache.canReadMore() && mCache.getNext() != null) {
11548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    mCache.clear();
11648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    mCache.close();
11748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    mCache = mCache.getNext();
11848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    mCache.resetRead();
11948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    return maybeReadSample();
12048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                } else {
12148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    if (mCacheReason == CACHE_REASON_RECORDED_PLAYBACK
12248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                            && !mCache.canReadMore() && mCache.getNext() == null) {
12348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                        // At the end of the recorded playback.
12448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                        setEos();
12548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    }
12648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    return false;
12748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                }
12848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            } else {
12948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                queueSample(sample);
13048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                return true;
13148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
13248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
13348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
13448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        public int dequeueSample(SampleHolder sample) {
13548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            maybeReadSample();
13648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            return super.dequeueSample(sample);
13748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
13848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
13948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        @Override
14048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        public void clear() {
14148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            super.clear();
14248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            for (SampleCache cache = mCache; cache != null; cache = cache.getNext()) {
14348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                cache.clear();
14448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                cache.close();
14548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
14648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mCache = null;
14748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
14848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
14948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        public long getSourceStartPositionUs() {
15048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            return mCache == null ? -1 : mCache.getStartPositionUs();
15148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
15248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
15348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
15448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    /**
15548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     * Creates {@link com.android.usbtuner.exoplayer.cache.CacheManager.SampleBuffer} with
15648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     * cached I/O backed by physical storage (e.g. trickplay,recording,recorded-playback).
15748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     *
15848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     * @param cacheManager
15948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     * @param cacheListener
16048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     * @param enableTrickplay {@code true} when trickplay should be enabled
16148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     * @param cacheReason the reason for caching samples {@link RecordingSampleBuffer.CacheReason}
16248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     */
16348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public RecordingSampleBuffer(CacheManager cacheManager, PlaybackCacheListener cacheListener,
16448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            boolean enableTrickplay, @CacheReason int cacheReason) {
16548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mCacheManager = cacheManager;
16648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mCacheListener = cacheListener;
16748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (cacheListener != null) {
16848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            cacheListener.onCacheStateChanged(enableTrickplay);
16948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
17048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mCacheReason = cacheReason;
17148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
17248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
17348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private String getTrackId(int index) {
17448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return mIds.get(index);
17548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
17648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
17748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Override
17848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public synchronized void init(List<String> ids, List<MediaFormat> mediaFormats)
17948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            throws IOException {
18048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mTrackCount = ids.size();
18148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (mTrackCount <= 0) {
18248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            throw new IOException("No tracks to initialize");
18348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
18448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mIds = ids;
18548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (mCacheReason == CACHE_REASON_RECORDING && mediaFormats == null) {
18648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            throw new IOException("MediaFormat is not provided.");
18748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
18848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mMediaFormats = mediaFormats;
18948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mSampleCaches = new SampleCache[mTrackCount];
19048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mPlayingSampleQueues = new CachedSampleQueue[mTrackCount];
19148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mCacheEndPositionUs = new long[mTrackCount];
19248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        for (int i = 0; i < mTrackCount; i++) {
19348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if (mCacheReason != CACHE_REASON_RECORDED_PLAYBACK) {
19448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mSampleCaches[i] = mCacheManager.createNewWriteFile(getTrackId(i), 0, mSamplePool);
19548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mPlayingSampleQueues[i] = null;
19648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mCacheEndPositionUs[i] = CHUNK_DURATION_US;
19748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            } else {
19848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mCacheManager.loadTrackFormStorage(mIds.get(i), mSamplePool);
19948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
20048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
20148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
20248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
20348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private boolean isLiveLocked(long positionUs) {
20448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        Long livePositionUs = null;
20548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        for (SampleCache cache : mSampleCaches) {
20648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if (livePositionUs == null || livePositionUs < cache.getEndPositionUs()) {
20748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                livePositionUs = cache.getEndPositionUs();
20848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
20948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
21048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return (livePositionUs == null
21148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                || Math.abs(livePositionUs - positionUs) < LIVE_THRESHOLD_US);
21248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
21348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
21448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private void seekIndividualTrackLocked(int index, long positionUs, boolean isLive) {
21548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        CachedSampleQueue queue = mPlayingSampleQueues[index];
21648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (queue == null) {
21748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            return;
21848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
21948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        queue.clear();
22048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (isLive) {
22148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            queue.setSource(mSampleCaches[index]);
22248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        } else {
22348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            queue.setSource(mCacheManager.getReadFile(getTrackId(index), positionUs));
22448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
22548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        queue.maybeReadSample();
22648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
22748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
22848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Override
22948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public synchronized void selectTrack(int index) {
23048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (mPlayingSampleQueues[index] == null) {
23148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            String trackId = getTrackId(index);
23248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mPlayingSampleQueues[index] = new CachedSampleQueue(mSamplePool);
23348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mCacheManager.registerEvictListener(trackId, this);
23448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            seekIndividualTrackLocked(index, mCurrentPlaybackPositionUs,
23548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    mCacheReason != CACHE_REASON_RECORDED_PLAYBACK && isLiveLocked(
23648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                            mCurrentPlaybackPositionUs));
23748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mPlayingSampleQueues[index].maybeReadSample();
23848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
23948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
24048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
24148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Override
24248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public synchronized void deselectTrack(int index) {
24348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (mPlayingSampleQueues[index] != null) {
24448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mPlayingSampleQueues[index].clear();
24548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mPlayingSampleQueues[index] = null;
24648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mCacheManager.unregisterEvictListener(getTrackId(index));
24748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
24848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
24948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
25048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Override
25148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public void writeSample(int index, SampleHolder sample,
25248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            ConditionVariable conditionVariable) throws IOException {
25348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        synchronized (this) {
25448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            SampleCache cache = mSampleCaches[index];
25548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if ((sample.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) {
25648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                if (sample.timeUs > mCacheDurationUs) {
25748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    mCacheDurationUs = sample.timeUs;
25848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                }
25948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                if (sample.timeUs >= mCacheEndPositionUs[index]) {
26048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    try {
26148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                        SampleCache nextCache = mCacheManager.createNewWriteFile(
26248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                getTrackId(index), mCacheEndPositionUs[index], mSamplePool);
26348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                        cache.finishWrite(nextCache);
26448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                        mSampleCaches[index] = cache = nextCache;
26548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                        mCacheEndPositionUs[index] =
26648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                ((sample.timeUs / CHUNK_DURATION_US) + 1) * CHUNK_DURATION_US;
26748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    } catch (IOException e) {
26848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                        cache.finishWrite(null);
26948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                        throw e;
27048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    }
27148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                }
27248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
27348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            cache.writeSample(sample, conditionVariable);
27448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
27548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
27648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (!conditionVariable.block(CACHE_WRITE_TIMEOUT_MS)) {
27748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            Log.e(TAG, "Error: Serious delay on writing cache");
27848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            conditionVariable.block();
27948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
28048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
28148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
28248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Override
28348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public boolean isWriteSpeedSlow(int sampleSize, long writeDurationNs) {
28448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (mCacheReason == CACHE_REASON_RECORDED_PLAYBACK) {
28548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            return false;
28648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
28748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mCacheManager.addWriteStat(sampleSize, writeDurationNs);
28848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return mCacheManager.isWriteSlow();
28948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
29048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
29148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Override
29248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public void handleWriteSpeedSlow() {
29348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        Log.w(TAG, "Disk is too slow for trickplay. Disable trickplay.");
29448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mCacheManager.disable();
29548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mCacheListener.onDiskTooSlow();
29648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
29748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
29848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Override
29948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public synchronized void setEos() {
30048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mEos = true;
30148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
30248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
30348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private synchronized boolean reachedEos() {
30448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return mEos;
30548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
30648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
30748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Override
30848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public synchronized int readSample(int track, SampleHolder sampleHolder) {
30948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        CachedSampleQueue queue = mPlayingSampleQueues[track];
31048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        Assert.assertNotNull(queue);
31148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        queue.maybeReadSample();
31248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        int result = queue.dequeueSample(sampleHolder);
31348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (result != SampleSource.SAMPLE_READ && reachedEos()) {
31448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            return SampleSource.END_OF_STREAM;
31548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
31648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return result;
31748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
31848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
31948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Override
32048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public synchronized void seekTo(long positionUs) {
32148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        boolean isLive = mCacheReason != CACHE_REASON_RECORDED_PLAYBACK && isLiveLocked(positionUs);
32248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
32348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        // Seek video track first
32448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        for (int i = 0; i < mPlayingSampleQueues.length; ++i) {
32548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            CachedSampleQueue queue = mPlayingSampleQueues[i];
32648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if (queue == null) {
32748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                continue;
32848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
32948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            seekIndividualTrackLocked(i, positionUs, isLive);
33048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if (DEBUG) {
33148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                Log.d(TAG, "start time = " + queue.getSourceStartPositionUs());
33248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
33348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
33448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mLastBufferedPositionUs = positionUs;
33548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
33648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
33748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Override
33848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public synchronized long getBufferedPositionUs() {
33948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        Long result = null;
34048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        for (CachedSampleQueue queue : mPlayingSampleQueues) {
34148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if (queue == null) {
34248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                continue;
34348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
34448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            Long bufferedPositionUs = queue.getEndPositionUs();
34548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if (bufferedPositionUs == null) {
34648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                continue;
34748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
34848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if (result == null || result > bufferedPositionUs) {
34948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                result = bufferedPositionUs;
35048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
35148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
35248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (result == null) {
35348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            return mLastBufferedPositionUs;
35448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        } else {
35548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            return (mLastBufferedPositionUs = result);
35648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
35748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
35848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
35948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Override
36048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public synchronized boolean continueBuffering(long positionUs) {
36148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        boolean hasSamples = true;
36248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mCurrentPlaybackPositionUs = positionUs;
36348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        for (CachedSampleQueue queue : mPlayingSampleQueues) {
36448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if (queue == null) {
36548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                continue;
36648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
36748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            queue.maybeReadSample();
36848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if (queue.isEmpty()) {
36948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                hasSamples = false;
37048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
37148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
37248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return hasSamples;
37348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
37448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
37548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Override
37648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public synchronized void release() {
37748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (mSampleCaches == null) {
37848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            return;
37948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
38048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (mCacheReason == CACHE_REASON_RECORDED_PLAYBACK) {
38148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mCacheManager.close();
38248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
38348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        for (int i = 0; i < mTrackCount; ++i) {
38448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if (mCacheReason != CACHE_REASON_RECORDED_PLAYBACK) {
38548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mSampleCaches[i].finishWrite(null);
38648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
38748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mCacheManager.unregisterEvictListener(getTrackId(i));
38848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
38948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (mCacheReason == CACHE_REASON_RECORDING && mTrackCount > 0) {
39048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            // Saves meta information for recording.
39148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            Pair<String, android.media.MediaFormat> audio = null, video = null;
39248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            for (int i = 0; i < mTrackCount; ++i) {
39348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                MediaFormat mediaFormat = mMediaFormats.get(i);
39448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
39548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mediaFormat.setLong(android.media.MediaFormat.KEY_DURATION, mCacheDurationUs);
39648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                if (MimeTypes.isAudio(mime)) {
39748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    audio = new Pair<>(getTrackId(i), mediaFormat);
39848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                }
39948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                else if (MimeTypes.isVideo(mime)) {
40048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    video = new Pair<>(getTrackId(i), mediaFormat);
40148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                }
40248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
40348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mCacheManager.writeMetaFiles(audio, video);
40448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
40548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
40648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        for (int i = 0; i < mTrackCount; ++i) {
40748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mCacheManager.clearTrack(getTrackId(i));
40848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
40948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
41048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
41148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    // CacheEvictListener
41248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Override
41348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public void onCacheEvicted(String id, long createdTimeMs) {
41448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (mCacheListener != null) {
41548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mCacheListener.onCacheStartTimeChanged(
41648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    createdTimeMs + TimeUnit.MICROSECONDS.toMillis(CHUNK_DURATION_US));
41748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
41848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
41948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho}
420