blob: fd7f8838bfd07e6f61cb5c8fc8975a800b1fdb45 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.usbtuner;
import android.content.Context;
import android.util.Log;
import com.android.tv.common.AutoCloseableUtils;
import com.android.usbtuner.ChannelScanFileParser.ScanChannel;
import com.android.usbtuner.data.Channel;
import com.android.usbtuner.data.TunerChannel;
import com.android.usbtuner.tvinput.EventDetector;
import com.android.usbtuner.tvinput.EventDetector.EventListener;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
/**
* A class that processes mpeg2ts stream coming from a tuner.
*/
public class UsbTunerTsScannerSource implements InputStreamSource {
// TODO: Refactor with {@link UsbTunerDataSource}.
private static final String TAG = "UsbTunerTsScannerSource";
private static final int MIN_READ_UNIT = 1500;
private static final int READ_BUFFER_SIZE = MIN_READ_UNIT * 10; // ~15KB
private boolean mStreaming;
private final TunerHal mTunerHal;
private Thread mStreamingThread;
private boolean mDeviceConfigured;
private final EventDetector mEventDetector;
private final AtomicLong mBytesFetched = new AtomicLong();
public UsbTunerTsScannerSource(Context context, EventListener eventListener) {
mTunerHal = TunerHal.createInstance(context);
if (mTunerHal == null) {
throw new RuntimeException("Failed to open a DVB device");
}
mEventDetector = new EventDetector(mTunerHal, eventListener);
}
/**
* Starts the streaming of a configured program. Throws a runtime exception if no channel and
* program have successfully been configured yet.
*/
@Override
public void startStream() {
if (!mDeviceConfigured) {
throw new RuntimeException("Channel and program not configured!");
}
mBytesFetched.set(0L);
mStreaming = true;
mStreamingThread = new StreamingThread();
mStreamingThread.start();
Log.i(TAG, "Streaming started");
}
@Override
public boolean setScanChannel(ScanChannel channel) {
if (mTunerHal.tune(channel.frequency, channel.modulation)) {
mEventDetector.startDetecting(channel.frequency, channel.modulation);
mDeviceConfigured = true;
return true;
}
return false;
}
@Override
public boolean tuneToChannel(TunerChannel channel) {
return false;
}
public List<TunerChannel> getIncompleteChannels() {
return mEventDetector.getIncompleteChannels();
}
/**
* Blocks the current thread until the streaming thread stops. In rare cases when the tuner
* device is overloaded this can take a while, but usually it returns pretty quickly.
*/
@Override
public void stopStream() {
mStreaming = false;
try {
if (mStreamingThread != null) {
mStreamingThread.join();
}
} catch (InterruptedException e) {
Log.e(TAG, "Interrupted while joining the streaming thread.", e);
}
}
@Override
public long getLimit() {
return mBytesFetched.get();
}
@Override
public long getPosition() {
return 0L;
}
@Override
public void close() {
AutoCloseableUtils.closeQuietly(mTunerHal);
}
private class StreamingThread extends Thread {
@Override
public void run() {
// Buffers for streaming data from the tuner and the internal buffer.
byte[] dataBuffer = new byte[READ_BUFFER_SIZE];
while (true) {
if (!mStreaming) {
break;
}
int bytesWritten;
bytesWritten = mTunerHal.readTsStream(dataBuffer, dataBuffer.length);
if (bytesWritten <= 0) {
continue;
}
mBytesFetched.addAndGet(bytesWritten);
mEventDetector.feedTSStream(dataBuffer, 0, bytesWritten);
}
Log.i(TAG, "Streaming stopped");
}
}
@Override
public int getType() {
return Channel.TYPE_TUNER;
}
}