aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/tv/tuner/source
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/tv/tuner/source')
-rw-r--r--src/com/android/tv/tuner/source/FileTsStreamer.java24
-rw-r--r--src/com/android/tv/tuner/source/TsDataSourceManager.java16
-rw-r--r--src/com/android/tv/tuner/source/TunerTsStreamer.java79
-rw-r--r--src/com/android/tv/tuner/source/TunerTsStreamerManager.java25
4 files changed, 114 insertions, 30 deletions
diff --git a/src/com/android/tv/tuner/source/FileTsStreamer.java b/src/com/android/tv/tuner/source/FileTsStreamer.java
index 14997ee4..f17dd46b 100644
--- a/src/com/android/tv/tuner/source/FileTsStreamer.java
+++ b/src/com/android/tv/tuner/source/FileTsStreamer.java
@@ -16,12 +16,14 @@
package com.android.tv.tuner.source;
+import android.content.Context;
import android.os.Environment;
import android.util.Log;
import android.util.SparseBooleanArray;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.upstream.DataSpec;
+import com.android.tv.Features;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.tuner.ChannelScanFileParser.ScanChannel;
import com.android.tv.tuner.data.TunerChannel;
@@ -60,6 +62,7 @@ public class FileTsStreamer implements TsStreamer {
private final Object mCircularBufferMonitor = new Object();
private final byte[] mCircularBuffer = new byte[CIRCULAR_BUFFER_SIZE];
private final FileSourceEventDetector mEventDetector;
+ private final Context mContext;
private long mBytesFetched;
private long mLastReadPosition;
@@ -120,8 +123,11 @@ public class FileTsStreamer implements TsStreamer {
* Creates {@link TsStreamer} for scanning & playing MPEG-2 TS file.
* @param eventListener the listener for channel & program information
*/
- public FileTsStreamer(EventDetector.EventListener eventListener) {
- mEventDetector = new FileSourceEventDetector(eventListener);
+ public FileTsStreamer(EventDetector.EventListener eventListener, Context context) {
+ mEventDetector =
+ new FileSourceEventDetector(
+ eventListener, Features.ENABLE_FILE_DVB.isEnabled(context));
+ mContext = context;
}
@Override
@@ -132,8 +138,12 @@ public class FileTsStreamer implements TsStreamer {
return false;
}
mEventDetector.start(mSource, FileSourceEventDetector.ALL_PROGRAM_NUMBERS);
- mSource.addPidFilter(TsParser.ATSC_SI_BASE_PID);
mSource.addPidFilter(TsParser.PAT_PID);
+ mSource.addPidFilter(TsParser.ATSC_SI_BASE_PID);
+ if (Features.ENABLE_FILE_DVB.isEnabled(mContext)) {
+ mSource.addPidFilter(TsParser.DVB_EIT_PID);
+ mSource.addPidFilter(TsParser.DVB_SDT_PID);
+ }
synchronized (mCircularBufferMonitor) {
if (mStreaming) {
return true;
@@ -160,8 +170,12 @@ public class FileTsStreamer implements TsStreamer {
mSource.addPidFilter(i);
}
mSource.addPidFilter(channel.getPcrPid());
- mSource.addPidFilter(TsParser.ATSC_SI_BASE_PID);
mSource.addPidFilter(TsParser.PAT_PID);
+ mSource.addPidFilter(TsParser.ATSC_SI_BASE_PID);
+ if (Features.ENABLE_FILE_DVB.isEnabled(mContext)) {
+ mSource.addPidFilter(TsParser.DVB_EIT_PID);
+ mSource.addPidFilter(TsParser.DVB_SDT_PID);
+ }
synchronized (mCircularBufferMonitor) {
if (mStreaming) {
return true;
@@ -256,7 +270,7 @@ public class FileTsStreamer implements TsStreamer {
* Returns whether the current pid filter is empty or not.
*/
public boolean isFilterEmpty() {
- return mPids.size() > 0;
+ return mPids.size() == 0;
}
/**
diff --git a/src/com/android/tv/tuner/source/TsDataSourceManager.java b/src/com/android/tv/tuner/source/TsDataSourceManager.java
index ccbb75ba..16be7582 100644
--- a/src/com/android/tv/tuner/source/TsDataSourceManager.java
+++ b/src/com/android/tv/tuner/source/TsDataSourceManager.java
@@ -17,9 +17,11 @@
package com.android.tv.tuner.source;
import android.content.Context;
+import android.support.annotation.VisibleForTesting;
-import com.android.tv.tuner.data.nano.Channel;
+import com.android.tv.tuner.TunerHal;
import com.android.tv.tuner.data.TunerChannel;
+import com.android.tv.tuner.data.nano.Channel;
import com.android.tv.tuner.tvinput.EventDetector;
import java.util.Map;
@@ -31,8 +33,6 @@ import java.util.concurrent.ConcurrentHashMap;
* One TsDataSourceManager should be created for per session.
*/
public class TsDataSourceManager {
- private static String TAG = "TsDataSourceManager";
-
private static final Object sLock = new Object();
private static final Map<TsDataSource, TsStreamer> sTsStreamers =
new ConcurrentHashMap<>();
@@ -80,7 +80,7 @@ public class TsDataSourceManager {
if (mIsRecording) {
return null;
}
- FileTsStreamer streamer = new FileTsStreamer(eventListener);
+ FileTsStreamer streamer = new FileTsStreamer(eventListener, context);
if (streamer.startStream(channel)) {
TsDataSource source = streamer.createDataSource();
sTsStreamers.put(source, streamer);
@@ -127,6 +127,14 @@ public class TsDataSourceManager {
}
/**
+ * Add tuner hal into TunerTsStreamerManager for test.
+ */
+ @VisibleForTesting
+ public void addTunerHalForTest(TunerHal tunerHal) {
+ mTunerStreamerManager.addTunerHal(tunerHal, mId);
+ }
+
+ /**
* Releases persistent resources.
*/
public void release() {
diff --git a/src/com/android/tv/tuner/source/TunerTsStreamer.java b/src/com/android/tv/tuner/source/TunerTsStreamer.java
index b24048e6..843cbdb7 100644
--- a/src/com/android/tv/tuner/source/TunerTsStreamer.java
+++ b/src/com/android/tv/tuner/source/TunerTsStreamer.java
@@ -18,6 +18,7 @@ package com.android.tv.tuner.source;
import android.content.Context;
import android.util.Log;
+import android.util.Pair;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.upstream.DataSpec;
@@ -30,6 +31,7 @@ import com.android.tv.tuner.tvinput.EventDetector;
import com.android.tv.tuner.tvinput.EventDetector.EventListener;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
@@ -42,23 +44,27 @@ public class TunerTsStreamer implements TsStreamer {
private static final int MIN_READ_UNIT = 1500;
private static final int READ_BUFFER_SIZE = MIN_READ_UNIT * 10; // ~15KB
private static final int CIRCULAR_BUFFER_SIZE = MIN_READ_UNIT * 20000; // ~ 30MB
+ private static final int TS_PACKET_SIZE = 188;
private static final int READ_TIMEOUT_MS = 5000; // 5 secs.
private static final int BUFFER_UNDERRUN_SLEEP_MS = 10;
+ private static final int READ_ERROR_STREAMING_ENDED = -1;
+ private static final int READ_ERROR_BUFFER_OVERWRITTEN = -2;
private final Object mCircularBufferMonitor = new Object();
private final byte[] mCircularBuffer = new byte[CIRCULAR_BUFFER_SIZE];
private long mBytesFetched;
private final AtomicLong mLastReadPosition = new AtomicLong();
- private boolean mEndOfStreamSent;
private boolean mStreaming;
private final TunerHal mTunerHal;
private TunerChannel mChannel;
private Thread mStreamingThread;
private final EventDetector mEventDetector;
+ private final List<Pair<EventListener, Boolean>> mEventListenerActions = new ArrayList<>();
private final TsStreamWriter mTsStreamWriter;
+ private String mChannelNumber;
public static class TunerDataSource extends TsDataSource {
private final TunerTsStreamer mTsStreamer;
@@ -103,6 +109,15 @@ public class TunerTsStreamer implements TsStreamer {
offset, readLength);
if (ret > 0) {
mLastReadPosition.addAndGet(ret);
+ } else if (ret == READ_ERROR_BUFFER_OVERWRITTEN) {
+ long currentPosition = mStartBufferedPosition + mLastReadPosition.get();
+ long endPosition = mTsStreamer.getBufferedPosition();
+ long diff = ((endPosition - currentPosition + TS_PACKET_SIZE - 1) / TS_PACKET_SIZE)
+ * TS_PACKET_SIZE;
+ Log.w(TAG, "Demux position jump by overwritten buffer: " + diff);
+ mStartBufferedPosition = currentPosition + diff;
+ mLastReadPosition.set(0);
+ return 0;
}
return ret;
}
@@ -114,7 +129,10 @@ public class TunerTsStreamer implements TsStreamer {
*/
public TunerTsStreamer(TunerHal tunerHal, EventListener eventListener, Context context) {
mTunerHal = tunerHal;
- mEventDetector = new EventDetector(mTunerHal, eventListener);
+ mEventDetector = new EventDetector(mTunerHal);
+ if (eventListener != null) {
+ mEventDetector.registerListener(eventListener);
+ }
mTsStreamWriter = context != null && TunerPreferences.getStoreTsStream(context) ?
new TsStreamWriter(context) : null;
}
@@ -125,7 +143,8 @@ public class TunerTsStreamer implements TsStreamer {
@Override
public boolean startStream(TunerChannel channel) {
- if (mTunerHal.tune(channel.getFrequency(), channel.getModulation())) {
+ if (mTunerHal.tune(channel.getFrequency(), channel.getModulation(),
+ channel.getDisplayNumber(false))) {
if (channel.hasVideo()) {
mTunerHal.addPidFilter(channel.getVideoPid(),
TunerHal.FILTER_TYPE_VIDEO);
@@ -148,6 +167,7 @@ public class TunerTsStreamer implements TsStreamer {
channel.getProgramNumber());
}
mChannel = channel;
+ mChannelNumber = channel.getDisplayNumber();
synchronized (mCircularBufferMonitor) {
if (mStreaming) {
Log.w(TAG, "Streaming should be stopped before start streaming");
@@ -156,7 +176,6 @@ public class TunerTsStreamer implements TsStreamer {
mStreaming = true;
mBytesFetched = 0;
mLastReadPosition.set(0L);
- mEndOfStreamSent = false;
}
if (mTsStreamWriter != null) {
mTsStreamWriter.setChannel(mChannel);
@@ -172,7 +191,7 @@ public class TunerTsStreamer implements TsStreamer {
@Override
public boolean startStream(ChannelScanFileParser.ScanChannel channel) {
- if (mTunerHal.tune(channel.frequency, channel.modulation)) {
+ if (mTunerHal.tune(channel.frequency, channel.modulation, null)) {
mEventDetector.startDetecting(
channel.frequency, channel.modulation, EventDetector.ALL_PROGRAM_NUMBERS);
synchronized (mCircularBufferMonitor) {
@@ -183,7 +202,6 @@ public class TunerTsStreamer implements TsStreamer {
mStreaming = true;
mBytesFetched = 0;
mLastReadPosition.set(0L);
- mEndOfStreamSent = false;
}
mStreamingThread = new StreamingThread();
mStreamingThread.start();
@@ -258,6 +276,26 @@ public class TunerTsStreamer implements TsStreamer {
}
}
+ public String getStreamerInfo() {
+ return "Channel: " + mChannelNumber + ", Streaming: " + mStreaming;
+ }
+
+ public void registerListener(EventListener listener) {
+ if (mEventDetector != null && listener != null) {
+ synchronized (mEventListenerActions) {
+ mEventListenerActions.add(new Pair<>(listener, true));
+ }
+ }
+ }
+
+ public void unregisterListener(EventListener listener) {
+ if (mEventDetector != null) {
+ synchronized (mEventListenerActions) {
+ mEventListenerActions.add(new Pair(listener, false));
+ }
+ }
+ }
+
private class StreamingThread extends Thread {
@Override
public void run() {
@@ -271,6 +309,20 @@ public class TunerTsStreamer implements TsStreamer {
}
}
+ if (mEventDetector != null) {
+ synchronized (mEventListenerActions) {
+ for (Pair listenerAction : mEventListenerActions) {
+ EventListener listener = (EventListener) listenerAction.first;
+ if ((boolean) listenerAction.second) {
+ mEventDetector.registerListener(listener);
+ } else {
+ mEventDetector.unregisterListener(listener);
+ }
+ }
+ mEventListenerActions.clear();
+ }
+ }
+
int bytesWritten = mTunerHal.readTsStream(dataBuffer, dataBuffer.length);
if (bytesWritten <= 0) {
try {
@@ -321,21 +373,14 @@ public class TunerTsStreamer implements TsStreamer {
* @throws IOException
*/
public int readAt(long pos, byte[] buffer, int offset, int amount) throws IOException {
- long readStartTime = System.currentTimeMillis();
while (true) {
synchronized (mCircularBufferMonitor) {
- if (mEndOfStreamSent || !mStreaming) {
- return -1;
+ if (!mStreaming) {
+ return READ_ERROR_STREAMING_ENDED;
}
if (mBytesFetched - CIRCULAR_BUFFER_SIZE > pos) {
- Log.e(TAG, "Demux is requesting the data which is already overwritten.");
- return -1;
- }
- if (System.currentTimeMillis() - readStartTime > READ_TIMEOUT_MS) {
- // Nothing was received during READ_TIMEOUT_MS before.
- mEndOfStreamSent = true;
- mCircularBufferMonitor.notifyAll();
- return -1;
+ Log.w(TAG, "Demux is requesting the data which is already overwritten.");
+ return READ_ERROR_BUFFER_OVERWRITTEN;
}
if (mBytesFetched < pos + amount) {
try {
diff --git a/src/com/android/tv/tuner/source/TunerTsStreamerManager.java b/src/com/android/tv/tuner/source/TunerTsStreamerManager.java
index cf1f6dcf..258a4d86 100644
--- a/src/com/android/tv/tuner/source/TunerTsStreamerManager.java
+++ b/src/com/android/tv/tuner/source/TunerTsStreamerManager.java
@@ -42,6 +42,7 @@ class TunerTsStreamerManager {
private final Object mCancelLock = new Object();
private final StreamerFinder mStreamerFinder = new StreamerFinder();
private final Map<Integer, TsStreamerCreator> mCreators = new HashMap<>();
+ private final Map<Integer, EventDetector.EventListener> mListeners = new HashMap<>();
private final Map<TsDataSource, TunerTsStreamer> mSourceToStreamerMap = new HashMap<>();
private final TunerHalManager mTunerHalManager = new TunerHalManager();
private static TunerTsStreamerManager sInstance;
@@ -68,6 +69,8 @@ class TunerTsStreamerManager {
mStreamerFinder.appendSessionLocked(channel, sessionId);
TunerTsStreamer streamer = mStreamerFinder.getStreamerLocked(channel);
TsDataSource source = streamer.createDataSource();
+ mListeners.put(sessionId, listener);
+ streamer.registerListener(listener);
mSourceToStreamerMap.put(source, streamer);
return source;
}
@@ -83,6 +86,7 @@ class TunerTsStreamerManager {
if (!creator.isCancelledLocked()) {
mStreamerFinder.putLocked(channel, sessionId, streamer);
TsDataSource source = streamer.createDataSource();
+ mListeners.put(sessionId, listener);
mSourceToStreamerMap.put(source, streamer);
return source;
}
@@ -104,6 +108,8 @@ class TunerTsStreamerManager {
if (streamer == null) {
return;
}
+ EventDetector.EventListener listener = mListeners.remove(sessionId);
+ streamer.unregisterListener(listener);
TunerChannel channel = streamer.getChannel();
SoftPreconditions.checkState(channel != null);
mStreamerFinder.removeSessionLocked(channel, sessionId);
@@ -125,6 +131,13 @@ class TunerTsStreamerManager {
}
}
+ /**
+ * Add tuner hal into TunerHalManager for test.
+ */
+ void addTunerHal(TunerHal tunerHal, int sessionId) {
+ mTunerHalManager.addTunerHal(tunerHal, sessionId);
+ }
+
synchronized void release(int sessionId) {
mTunerHalManager.releaseCachedHal(sessionId);
}
@@ -261,16 +274,16 @@ class TunerTsStreamerManager {
}
private void releaseTunerHal(TunerHal hal, int sessionId, boolean reuse) {
- if (!reuse) {
+ if (!reuse || !hal.isReusable()) {
AutoCloseableUtils.closeQuietly(hal);
return;
}
TunerHal cachedHal = mTunerHals.get(sessionId);
if (cachedHal != hal) {
mTunerHals.put(sessionId, hal);
- }
- if (cachedHal != null && cachedHal != hal) {
- AutoCloseableUtils.closeQuietly(cachedHal);
+ if (cachedHal != null) {
+ AutoCloseableUtils.closeQuietly(cachedHal);
+ }
}
}
@@ -283,5 +296,9 @@ class TunerTsStreamerManager {
AutoCloseableUtils.closeQuietly(hal);
}
}
+
+ private void addTunerHal(TunerHal tunerHal, int sessionId) {
+ mTunerHals.put(sessionId, tunerHal);
+ }
}
} \ No newline at end of file