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
1765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkopackage com.android.tv.tuner.tvinput;
181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
191abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.Log;
201abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.SparseArray;
211abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.SparseBooleanArray;
221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.TunerHal;
2465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.ts.TsParser;
2565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsiData;
2665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData;
276ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.tuner.data.TunerChannel;
286ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
296ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
311abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.ArrayList;
321abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.HashSet;
331abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.List;
341abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.Set;
351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/**
371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Detects channels and programs that are emerged or changed while parsing ATSC PSIP information.
381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
391abddd9f6225298066094e20a6c29061b6af4590Nick Chalkopublic class EventDetector {
401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String TAG = "EventDetector";
411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final boolean DEBUG = false;
4265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public static final int ALL_PROGRAM_NUMBERS = -1;
431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
44ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private final TunerHal mTunerHal;
451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private TsParser mTsParser;
471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private final Set<Integer> mPidSet = new HashSet<>();
481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // To prevent channel duplication
501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private final Set<Integer> mVctProgramNumberSet = new HashSet<>();
516ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private final Set<Integer> mSdtProgramNumberSet = new HashSet<>();
521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private final SparseArray<TunerChannel> mChannelMap = new SparseArray<>();
531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private final SparseBooleanArray mVctCaptionTracksFound = new SparseBooleanArray();
541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private final SparseBooleanArray mEitCaptionTracksFound = new SparseBooleanArray();
556ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private final List<EventListener> mEventListeners = new ArrayList<>();
561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private int mFrequency;
571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private String mModulation;
5865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private int mProgramNumber = ALL_PROGRAM_NUMBERS;
591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
6065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private final TsParser.TsOutputListener mTsOutputListener = new TsParser.TsOutputListener() {
611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
6265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        public void onPatDetected(List<PsiData.PatItem> items) {
6365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            for (PsiData.PatItem i : items) {
64d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                if (mProgramNumber == ALL_PROGRAM_NUMBERS || mProgramNumber == i.getProgramNo()) {
6565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    mTunerHal.addPidFilter(i.getPmtPid(), TunerHal.FILTER_TYPE_OTHER);
6665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                }
671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public void onEitPidDetected(int pid) {
721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            startListening(pid);
731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
7665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        public void onEitItemParsed(PsipData.VctItem channel, List<PsipData.EitItem> items) {
771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            TunerChannel tunerChannel = mChannelMap.get(channel.getProgramNumber());
781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (DEBUG) {
791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.d(TAG, "onEitItemParsed tunerChannel:" + tunerChannel + " "
801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        + channel.getProgramNumber());
811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int channelSourceId = channel.getSourceId();
831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // Source id 0 is useful for cases where a cable operator wishes to define a channel for
851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // which no EPG data is currently available.
861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // We don't handle such a case.
871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (channelSourceId == 0) {
881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return;
891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // If at least a one caption track have been found in EIT items for the given channel,
921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // we starts to interpret the zero tracks as a clearance of the caption tracks.
931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean captionTracksFound = mEitCaptionTracksFound.get(channelSourceId);
9465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            for (PsipData.EitItem item : items) {
951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (captionTracksFound) {
961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    break;
971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                List<AtscCaptionTrack> captionTracks = item.getCaptionTracks();
991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (captionTracks != null && !captionTracks.isEmpty()) {
1001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    captionTracksFound = true;
1011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
1021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mEitCaptionTracksFound.put(channelSourceId, captionTracksFound);
1041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (captionTracksFound) {
10565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                for (PsipData.EitItem item : items) {
1061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    item.setHasCaptionTrack();
1071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
1081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1096ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (tunerChannel != null && !mEventListeners.isEmpty()) {
1106ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                for (EventListener eventListener : mEventListeners) {
1116ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    eventListener.onEventDetected(tunerChannel, items);
1126ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                }
1131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
1171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public void onEttPidDetected(int pid) {
1181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            startListening(pid);
1191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
12265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        public void onAllVctItemsParsed() {
1236ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (!mEventListeners.isEmpty()) {
1246ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                for (EventListener eventListener : mEventListeners) {
1256ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    eventListener.onChannelScanDone();
1266ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                }
12765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
12865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
12965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
13065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        @Override
13165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        public void onVctItemParsed(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems) {
1321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (DEBUG) {
1331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.d(TAG, "onVctItemParsed VCT " + channel);
1341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.d(TAG, "                PMT " + pmtItems);
1351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // Merges the audio and caption tracks located in PMT items into the tracks of the given
1381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // tuner channel.
1391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            TunerChannel tunerChannel = new TunerChannel(channel, pmtItems);
1401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            List<AtscAudioTrack> audioTracks = new ArrayList<>();
1411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            List<AtscCaptionTrack> captionTracks = new ArrayList<>();
14265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            for (PsiData.PmtItem pmtItem : pmtItems) {
1431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (pmtItem.getAudioTracks() != null) {
1441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    audioTracks.addAll(pmtItem.getAudioTracks());
1451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
1461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (pmtItem.getCaptionTracks() != null) {
1471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    captionTracks.addAll(pmtItem.getCaptionTracks());
1481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
1491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int channelProgramNumber = channel.getProgramNumber();
1511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // If at least a one caption track have been found in VCT items for the given channel,
1531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // we starts to interpret the zero tracks as a clearance of the caption tracks.
1541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean captionTracksFound = mVctCaptionTracksFound.get(channelProgramNumber)
1551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    || !captionTracks.isEmpty();
1561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mVctCaptionTracksFound.put(channelProgramNumber, captionTracksFound);
1571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (captionTracksFound) {
1581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                tunerChannel.setHasCaptionTrack();
1591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            tunerChannel.setAudioTracks(audioTracks);
1611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            tunerChannel.setCaptionTracks(captionTracks);
1621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            tunerChannel.setFrequency(mFrequency);
1631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            tunerChannel.setModulation(mModulation);
1641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mChannelMap.put(tunerChannel.getProgramNumber(), tunerChannel);
1651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean found = mVctProgramNumberSet.contains(channelProgramNumber);
1661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (!found) {
1671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                mVctProgramNumberSet.add(channelProgramNumber);
1681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1696ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (!mEventListeners.isEmpty()) {
1706ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                for (EventListener eventListener : mEventListeners) {
1716ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    eventListener.onChannelDetected(tunerChannel, !found);
1726ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                }
1736ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
1746ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
1756ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
1766ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        @Override
1776ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        public void onSdtItemParsed(PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems) {
1786ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (DEBUG) {
1796ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                Log.d(TAG, "onSdtItemParsed SDT " + channel);
1806ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                Log.d(TAG, "                PMT " + pmtItems);
1816ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
1826ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
1836ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            // Merges the audio and caption tracks located in PMT items into the tracks of the given
1846ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            // tuner channel.
1856ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            TunerChannel tunerChannel = new TunerChannel(channel, pmtItems);
1866ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            List<AtscAudioTrack> audioTracks = new ArrayList<>();
1876ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            List<AtscCaptionTrack> captionTracks = new ArrayList<>();
1886ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            for (PsiData.PmtItem pmtItem : pmtItems) {
1896ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                if (pmtItem.getAudioTracks() != null) {
1906ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    audioTracks.addAll(pmtItem.getAudioTracks());
1916ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                }
1926ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                if (pmtItem.getCaptionTracks() != null) {
1936ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    captionTracks.addAll(pmtItem.getCaptionTracks());
1946ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                }
1956ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
1966ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            int channelProgramNumber = channel.getServiceId();
1976ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            tunerChannel.setAudioTracks(audioTracks);
1986ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            tunerChannel.setCaptionTracks(captionTracks);
1996ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            tunerChannel.setFrequency(mFrequency);
2006ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            tunerChannel.setModulation(mModulation);
2016ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mChannelMap.put(tunerChannel.getProgramNumber(), tunerChannel);
2026ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            boolean found = mSdtProgramNumberSet.contains(channelProgramNumber);
2036ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (!found) {
2046ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                mSdtProgramNumberSet.add(channelProgramNumber);
2056ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
2066ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (!mEventListeners.isEmpty()) {
2076ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                for (EventListener eventListener : mEventListeners) {
2086ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    eventListener.onChannelDetected(tunerChannel, !found);
2096ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                }
2101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
2111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    };
2131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    /**
2151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * Listener for detecting ATSC TV channels and receiving EPG data.
2161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     */
2171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public interface EventListener {
2181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        /**
2201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko         * Fired when new information of an ATSC TV channel arrived.
2211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko         *
2221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko         * @param channel an ATSC TV channel
2231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko         * @param channelArrivedAtFirstTime tells whether this channel arrived at first time
2241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko         */
2251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        void onChannelDetected(TunerChannel channel, boolean channelArrivedAtFirstTime);
2261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        /**
2281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko         * Fired when new program events of an ATSC TV channel arrived.
2291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko         *
2301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko         * @param channel an ATSC TV channel
2311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko         * @param items a list of EIT items that were received
2321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko         */
23365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        void onEventDetected(TunerChannel channel, List<PsipData.EitItem> items);
23465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
23565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        /**
23665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko         * Fired when information of all detectable ATSC TV channels in current frequency arrived.
23765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko         */
23865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        void onChannelScanDone();
2391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
24165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
24265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Creates a detector for ATSC TV channles and program information.
2436ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     *
24465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * @param usbTunerInteface {@link TunerHal}
24565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
2466ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public EventDetector(TunerHal usbTunerInteface) {
247ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mTunerHal = usbTunerInteface;
2481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private void reset() {
2516ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        // TODO: Use TsParser.reset()
2526ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        int deliverySystemType = mTunerHal.getDeliverySystemType();
2536ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        mTsParser =
2546ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                new TsParser(
2556ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                        mTsOutputListener,
2566ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                        TunerHal.isDvbDeliverySystem(mTunerHal.getDeliverySystemType()));
2571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mPidSet.clear();
2581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mVctProgramNumberSet.clear();
2596ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        mSdtProgramNumberSet.clear();
2601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mVctCaptionTracksFound.clear();
2611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mEitCaptionTracksFound.clear();
2621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mChannelMap.clear();
2631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
26565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
26665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Starts detecting channel and program information.
26765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     *
26865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * @param frequency The frequency to listen to.
26965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * @param modulation The modulation type.
27065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * @param programNumber The program number if this is for handling tune request. For scanning
27165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     *            purpose, supply {@link #ALL_PROGRAM_NUMBERS}.
27265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
27365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public void startDetecting(int frequency, String modulation, int programNumber) {
2741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        reset();
2751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mFrequency = frequency;
2761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mModulation = modulation;
27765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        mProgramNumber = programNumber;
2781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private void startListening(int pid) {
2811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mPidSet.contains(pid)) {
2821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return;
2831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mPidSet.add(pid);
285ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mTunerHal.addPidFilter(pid, TunerHal.FILTER_TYPE_OTHER);
2861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
28865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
28965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Feeds ATSC TS stream to detect channel and program information.
29065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * @param data buffer for ATSC TS stream
29165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * @param startOffset the offset where buffer starts
29265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * @param length The length of available data
29365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
2941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public void feedTSStream(byte[] data, int startOffset, int length) {
2951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mPidSet.isEmpty()) {
2961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            startListening(TsParser.ATSC_SI_BASE_PID);
2971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mTsParser != null) {
2991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mTsParser.feedTSData(data, startOffset, length);
3001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
3021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
30365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
30465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Retrieves the channel information regardless of being well-formed.
30565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * @return {@link List} of {@link TunerChannel}
30665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
30765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public List<TunerChannel> getMalFormedChannels() {
30865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        return mTsParser.getMalFormedChannels();
3091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
3106ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
3116ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    /**
3126ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * Registers an EventListener.
3136ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * @param eventListener the listener to be registered
3146ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     */
3156ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public void registerListener(EventListener eventListener) {
3166ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (mTsParser != null) {
3176ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            // Resets the version numbers so that the new listener can receive the EIT items.
3186ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            // Otherwise, each EIT session is handled only once unless there is a new version.
3196ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mTsParser.resetDataVersions();
3206ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
3216ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        mEventListeners.add(eventListener);
3226ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
3236ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
3246ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    /**
3256ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * Unregisters an EventListener.
3266ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * @param eventListener the listener to be unregistered
3276ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     */
3286ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public void unregisterListener(EventListener eventListener) {
3296ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        boolean removed = mEventListeners.remove(eventListener);
3306ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (!removed && DEBUG) {
3316ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            Log.d(TAG, "Cannot unregister a non-registered listener!");
3326ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
3336ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
3341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko}
335