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; 181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 191abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.content.Context; 201abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.Log; 211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 22ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport com.android.tv.common.AutoCloseableUtils; 231abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.usbtuner.ChannelScanFileParser.ScanChannel; 241abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.usbtuner.data.Channel; 251abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.usbtuner.data.TunerChannel; 261abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.usbtuner.tvinput.EventDetector; 271abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.usbtuner.tvinput.EventDetector.EventListener; 281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 291abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.List; 301abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.concurrent.atomic.AtomicLong; 311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/** 331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * A class that processes mpeg2ts stream coming from a tuner. 341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */ 351abddd9f6225298066094e20a6c29061b6af4590Nick Chalkopublic class UsbTunerTsScannerSource implements InputStreamSource { 361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // TODO: Refactor with {@link UsbTunerDataSource}. 371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final String TAG = "UsbTunerTsScannerSource"; 391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int MIN_READ_UNIT = 1500; 411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int READ_BUFFER_SIZE = MIN_READ_UNIT * 10; // ~15KB 421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private boolean mStreaming; 441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 45ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private final TunerHal mTunerHal; 461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private Thread mStreamingThread; 471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private boolean mDeviceConfigured; 481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final EventDetector mEventDetector; 491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final AtomicLong mBytesFetched = new AtomicLong(); 501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public UsbTunerTsScannerSource(Context context, EventListener eventListener) { 5248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mTunerHal = TunerHal.createInstance(context); 53ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (mTunerHal == null) { 541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko throw new RuntimeException("Failed to open a DVB device"); 551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 56ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko mEventDetector = new EventDetector(mTunerHal, eventListener); 571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko /** 601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Starts the streaming of a configured program. Throws a runtime exception if no channel and 611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * program have successfully been configured yet. 621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */ 631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void startStream() { 651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (!mDeviceConfigured) { 661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko throw new RuntimeException("Channel and program not configured!"); 671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mBytesFetched.set(0L); 701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mStreaming = true; 711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mStreamingThread = new StreamingThread(); 721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mStreamingThread.start(); 731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.i(TAG, "Streaming started"); 741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public boolean setScanChannel(ScanChannel channel) { 78ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (mTunerHal.tune(channel.frequency, channel.modulation)) { 791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mEventDetector.startDetecting(channel.frequency, channel.modulation); 801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mDeviceConfigured = true; 811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return true; 821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return false; 841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public boolean tuneToChannel(TunerChannel channel) { 881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return false; 891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public List<TunerChannel> getIncompleteChannels() { 921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return mEventDetector.getIncompleteChannels(); 931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko /** 961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Blocks the current thread until the streaming thread stops. In rare cases when the tuner 971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * device is overloaded this can take a while, but usually it returns pretty quickly. 981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */ 991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void stopStream() { 1011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mStreaming = false; 1021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko try { 1041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mStreamingThread != null) { 1051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mStreamingThread.join(); 1061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } catch (InterruptedException e) { 1081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.e(TAG, "Interrupted while joining the streaming thread.", e); 1091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public long getLimit() { 1141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return mBytesFetched.get(); 1151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public long getPosition() { 1191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return 0L; 1201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 123ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko public void close() { 124ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko AutoCloseableUtils.closeQuietly(mTunerHal); 1251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private class StreamingThread extends Thread { 1281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void run() { 1311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Buffers for streaming data from the tuner and the internal buffer. 1321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko byte[] dataBuffer = new byte[READ_BUFFER_SIZE]; 1331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko while (true) { 1351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (!mStreaming) { 1361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 1371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int bytesWritten; 139ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko bytesWritten = mTunerHal.readTsStream(dataBuffer, dataBuffer.length); 1401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (bytesWritten <= 0) { 1411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko continue; 1421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mBytesFetched.addAndGet(bytesWritten); 1441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mEventDetector.feedTSStream(dataBuffer, 0, bytesWritten); 1461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.i(TAG, "Streaming stopped"); 1491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public int getType() { 1541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return Channel.TYPE_TUNER; 1551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko} 157