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; 1848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 1948dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.media.MediaDataSource; 2048dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.media.MediaExtractor; 2148dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.os.ConditionVariable; 2248dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.os.SystemClock; 2348dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.util.Log; 2448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 2548dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.google.android.exoplayer.SampleHolder; 2648dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.usbtuner.exoplayer.cache.CacheManager; 2748dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.usbtuner.exoplayer.cache.RecordingSampleBuffer; 2848dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.usbtuner.tvinput.PlaybackCacheListener; 2948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 3048dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport java.io.IOException; 3148dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport java.util.ArrayList; 3248dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport java.util.List; 3348dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport java.util.Locale; 3448dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport java.util.concurrent.atomic.AtomicLong; 3548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 3648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho/** 3748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * Records live streams on the disk for DVR. 3848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho */ 3948dadb49248271b01997862e1335912a4f2e189fYoungsang Chopublic class Recorder { 4048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private static final String TAG = "Recorder"; 4148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 4248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho // Maximum bandwidth of 1080p channel is about 2.2MB/s. 2MB for a sample will suffice. 4348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private static final int SAMPLE_BUFFER_SIZE = 1024 * 1024 * 2; 4448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private static final AtomicLong ID_COUNTER = new AtomicLong(0); 4548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 4648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private final MediaDataSource mDataSource; 4748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private final MediaExtractor mMediaExtractor; 4848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private final ExtractorThread mExtractorThread; 4948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private int mTrackCount; 5048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private List<android.media.MediaFormat> mMediaFormats; 5148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 5248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private final CacheManager.SampleBuffer mSampleBuffer; 5348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 5448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private boolean mReleased = false; 5548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private boolean mResultNotified = false; 5648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private final long mId; 5748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 5848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private final RecordListener mRecordListener; 5948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 6048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho /** 6148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * Listeners for events which happens during the recording. 6248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho */ 6348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public interface RecordListener { 6448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 6548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho /** 6648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * Notifies recording completion. 6748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * 6848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * @param success {@code true} when the recording succeeded, {@code false} otherwise 6948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho */ 7048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho void notifyRecordingFinished(boolean success); 7148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 7248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 7348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho /** 7448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * Create a recorder for a {@link android.media.MediaDataSource}. 7548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * 7648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * @param source {@link android.media.MediaDataSource} to record from 7748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * @param cacheManager the manager for recording samples to physical storage 7848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * @param cacheListener the {@link com.android.usbtuner.tvinput.PlaybackCacheListener} 7948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * to notify cache storage status change 8048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * @param recordListener RecordListener to notify events during the recording 8148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho */ 8248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public Recorder(MediaDataSource source, CacheManager cacheManager, 8348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho PlaybackCacheListener cacheListener, RecordListener recordListener) { 8448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mDataSource = source; 8548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mMediaExtractor = new MediaExtractor(); 8648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mExtractorThread = new ExtractorThread(); 8748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mRecordListener = recordListener; 8848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 8948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mSampleBuffer = new RecordingSampleBuffer(cacheManager, cacheListener, false, 9048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho RecordingSampleBuffer.CACHE_REASON_RECORDING); 9148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mId = ID_COUNTER.incrementAndGet(); 9248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 9348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 9448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private class ExtractorThread extends Thread { 9548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private volatile boolean mQuitRequested = false; 9648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 9748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public ExtractorThread() { 9848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho super("ExtractorThread"); 9948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 10048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 10148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho @Override 10248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public void run() { 10348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho SampleHolder sample = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL); 10448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho sample.ensureSpaceForWrite(SAMPLE_BUFFER_SIZE); 10548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho ConditionVariable conditionVariable = new ConditionVariable(); 10648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho while (!mQuitRequested) { 10748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho fetchSample(sample, conditionVariable); 10848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 10948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho cleanUp(); 11048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 11148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 11248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private void fetchSample(SampleHolder sample, ConditionVariable conditionVariable) { 11348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho int index = mMediaExtractor.getSampleTrackIndex(); 11448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho if (index < 0) { 11548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho Log.i(TAG, "EoS"); 11648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mQuitRequested = true; 11748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mSampleBuffer.setEos(); 11848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho return; 11948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 12048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho sample.data.clear(); 12148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho sample.size = mMediaExtractor.readSampleData(sample.data, 0); 12248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho if (sample.size < 0 || sample.size > SAMPLE_BUFFER_SIZE) { 12348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho // Should not happen 12448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho Log.e(TAG, "Invalid sample size: " + sample.size); 12548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mMediaExtractor.advance(); 12648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho return; 12748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 12848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho sample.data.position(sample.size); 12948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho sample.timeUs = mMediaExtractor.getSampleTime(); 13048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho sample.flags = mMediaExtractor.getSampleFlags(); 13148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 13248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mMediaExtractor.advance(); 13348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho try { 13448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho queueSample(index, sample, conditionVariable); 13548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } catch (IOException e) { 13648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mQuitRequested = true; 13748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mSampleBuffer.setEos(); 13848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 13948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 14048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 14148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public void quit() { 14248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mQuitRequested = true; 14348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 14448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 14548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 14648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private void queueSample(int index, SampleHolder sample, ConditionVariable conditionVariable) 14748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho throws IOException { 14848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho long writeStartTimeNs = SystemClock.elapsedRealtimeNanos(); 14948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mSampleBuffer.writeSample(index, sample, conditionVariable); 15048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 15148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho // Check if the storage has enough bandwidth for recording. Otherwise we disable it 15248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho // and notify the slowness. 15348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho if (mSampleBuffer.isWriteSpeedSlow(sample.size, 15448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho SystemClock.elapsedRealtimeNanos() - writeStartTimeNs)) { 15548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho Log.w(TAG, "Disk is too slow for trickplay. Disable trickplay."); 15648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho throw new IOException("Disk is too slow"); 15748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 15848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 15948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 16048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho /** 16148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * Prepares a recording. 16248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * 16348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * @return {@code true} when preparation finished successfully, {@code false} otherwise 16448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * @throws IOException 16548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho */ 16648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public boolean prepare() throws IOException { 16748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho synchronized (this) { 16848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mMediaExtractor.setDataSource(mDataSource); 16948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 17048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mTrackCount = mMediaExtractor.getTrackCount(); 17148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho List<String> ids = new ArrayList<>(); 17248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mMediaFormats = new ArrayList<>(); 17348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho for (int i = 0; i < mTrackCount; i++) { 17448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho ids.add(String.format(Locale.ENGLISH, "%s_%x", Long.toHexString(mId), i)); 17548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho android.media.MediaFormat format = mMediaExtractor.getTrackFormat(i); 17648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mMediaExtractor.selectTrack(i); 17748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mMediaFormats.add(format); 17848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 17948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mSampleBuffer.init(ids, mMediaFormats); 18048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 18148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mExtractorThread.start(); 18248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho return true; 18348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 18448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 18548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho /** 18648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho * Releases all the resources which were used in the recording. 18748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho */ 18848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho public void release() { 18948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho synchronized (this) { 19048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mReleased = true; 19148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 19248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho if (mExtractorThread.isAlive()) { 19348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mExtractorThread.quit(); 19448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho // We don't join here to prevent hang --- MediaExtractor is released at the thread. 19548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } else { 19648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho cleanUp(); 19748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 19848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 19948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 20048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private synchronized void cleanUp() { 20148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho if (!mReleased) { 20248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho if (!mResultNotified) { 20348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mRecordListener.notifyRecordingFinished(false); 20448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mResultNotified = true; 20548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 20648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho return; 20748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 20848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mSampleBuffer.release(); 20948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho if (!mResultNotified) { 21048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mRecordListener.notifyRecordingFinished(true); 21148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mResultNotified = true; 21248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 21348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mMediaExtractor.release(); 21448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho } 21548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho 21648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho} 217