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