• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.usbtuner.tvinput;
18 
19 import android.util.Log;
20 import android.util.SparseArray;
21 import android.util.SparseBooleanArray;
22 
23 import com.android.usbtuner.TunerHal;
24 import com.android.usbtuner.data.PsiData.PatItem;
25 import com.android.usbtuner.data.PsiData.PmtItem;
26 import com.android.usbtuner.data.PsipData.EitItem;
27 import com.android.usbtuner.data.PsipData.VctItem;
28 import com.android.usbtuner.data.Track.AtscAudioTrack;
29 import com.android.usbtuner.data.Track.AtscCaptionTrack;
30 import com.android.usbtuner.data.TunerChannel;
31 import com.android.usbtuner.ts.TsParser;
32 import com.android.usbtuner.ts.TsParser.TsOutputListener;
33 
34 import java.util.ArrayList;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Set;
38 
39 /**
40  * Detects channels and programs that are emerged or changed while parsing ATSC PSIP information.
41  */
42 public class EventDetector {
43     private static final String TAG = "EventDetector";
44     private static final boolean DEBUG = false;
45 
46     private final TunerHal mTunerHal;
47 
48     private TsParser mTsParser;
49     private final Set<Integer> mPidSet = new HashSet<>();
50 
51     // To prevent channel duplication
52     private final Set<Integer> mVctProgramNumberSet = new HashSet<>();
53     private final SparseArray<TunerChannel> mChannelMap = new SparseArray<>();
54     private final SparseBooleanArray mVctCaptionTracksFound = new SparseBooleanArray();
55     private final SparseBooleanArray mEitCaptionTracksFound = new SparseBooleanArray();
56     private EventListener mEventListener;
57     private int mFrequency;
58     private String mModulation;
59 
60     private TsOutputListener mTsOutputListener = new TsOutputListener() {
61         @Override
62         public void onPatDetected(List<PatItem> items) {
63             for (PatItem i : items) {
64                 mTunerHal.addPidFilter(i.getPmtPid(), TunerHal.FILTER_TYPE_OTHER);
65             }
66         }
67 
68         @Override
69         public void onEitPidDetected(int pid) {
70             startListening(pid);
71         }
72 
73         @Override
74         public void onEitItemParsed(VctItem channel, List<EitItem> items) {
75             TunerChannel tunerChannel = mChannelMap.get(channel.getProgramNumber());
76             if (DEBUG) {
77                 Log.d(TAG, "onEitItemParsed tunerChannel:" + tunerChannel + " "
78                         + channel.getProgramNumber());
79             }
80             int channelSourceId = channel.getSourceId();
81 
82             // Source id 0 is useful for cases where a cable operator wishes to define a channel for
83             // which no EPG data is currently available.
84             // We don't handle such a case.
85             if (channelSourceId == 0) {
86                 return;
87             }
88 
89             // If at least a one caption track have been found in EIT items for the given channel,
90             // we starts to interpret the zero tracks as a clearance of the caption tracks.
91             boolean captionTracksFound = mEitCaptionTracksFound.get(channelSourceId);
92             for (EitItem item : items) {
93                 if (captionTracksFound) {
94                     break;
95                 }
96                 List<AtscCaptionTrack> captionTracks = item.getCaptionTracks();
97                 if (captionTracks != null && !captionTracks.isEmpty()) {
98                     captionTracksFound = true;
99                 }
100             }
101             mEitCaptionTracksFound.put(channelSourceId, captionTracksFound);
102             if (captionTracksFound) {
103                 for (EitItem item : items) {
104                     item.setHasCaptionTrack();
105                 }
106             }
107             if (tunerChannel != null && mEventListener != null) {
108                 mEventListener.onEventDetected(tunerChannel, items);
109             }
110         }
111 
112         @Override
113         public void onEttPidDetected(int pid) {
114             startListening(pid);
115         }
116 
117         @Override
118         public void onVctItemParsed(VctItem channel, List<PmtItem> pmtItems) {
119             if (DEBUG) {
120                 Log.d(TAG, "onVctItemParsed VCT " + channel);
121                 Log.d(TAG, "                PMT " + pmtItems);
122             }
123 
124             // Merges the audio and caption tracks located in PMT items into the tracks of the given
125             // tuner channel.
126             TunerChannel tunerChannel = new TunerChannel(channel, pmtItems);
127             List<AtscAudioTrack> audioTracks = new ArrayList<>();
128             List<AtscCaptionTrack> captionTracks = new ArrayList<>();
129             for (PmtItem pmtItem : pmtItems) {
130                 if (pmtItem.getAudioTracks() != null) {
131                     audioTracks.addAll(pmtItem.getAudioTracks());
132                 }
133                 if (pmtItem.getCaptionTracks() != null) {
134                     captionTracks.addAll(pmtItem.getCaptionTracks());
135                 }
136             }
137             int channelProgramNumber = channel.getProgramNumber();
138 
139             // If at least a one caption track have been found in VCT items for the given channel,
140             // we starts to interpret the zero tracks as a clearance of the caption tracks.
141             boolean captionTracksFound = mVctCaptionTracksFound.get(channelProgramNumber)
142                     || !captionTracks.isEmpty();
143             mVctCaptionTracksFound.put(channelProgramNumber, captionTracksFound);
144             if (captionTracksFound) {
145                 tunerChannel.setHasCaptionTrack();
146             }
147             tunerChannel.setAudioTracks(audioTracks);
148             tunerChannel.setCaptionTracks(captionTracks);
149             tunerChannel.setFrequency(mFrequency);
150             tunerChannel.setModulation(mModulation);
151             mChannelMap.put(tunerChannel.getProgramNumber(), tunerChannel);
152             boolean found = mVctProgramNumberSet.contains(channelProgramNumber);
153             if (!found) {
154                 mVctProgramNumberSet.add(channelProgramNumber);
155             }
156             if (mEventListener != null) {
157                 mEventListener.onChannelDetected(tunerChannel, !found);
158             }
159         }
160     };
161 
162     /**
163      * Listener for detecting ATSC TV channels and receiving EPG data.
164      */
165     public interface EventListener {
166 
167         /**
168          * Fired when new information of an ATSC TV channel arrived.
169          *
170          * @param channel an ATSC TV channel
171          * @param channelArrivedAtFirstTime tells whether this channel arrived at first time
172          */
onChannelDetected(TunerChannel channel, boolean channelArrivedAtFirstTime)173         void onChannelDetected(TunerChannel channel, boolean channelArrivedAtFirstTime);
174 
175         /**
176          * Fired when new program events of an ATSC TV channel arrived.
177          *
178          * @param channel an ATSC TV channel
179          * @param items a list of EIT items that were received
180          */
onEventDetected(TunerChannel channel, List<EitItem> items)181         void onEventDetected(TunerChannel channel, List<EitItem> items);
182     }
183 
EventDetector(TunerHal usbTunerInteface, EventListener listener)184     public EventDetector(TunerHal usbTunerInteface, EventListener listener) {
185         mTunerHal = usbTunerInteface;
186         mEventListener = listener;
187     }
188 
reset()189     private void reset() {
190         mTsParser = new TsParser(mTsOutputListener); // TODO: Use TsParser.reset()
191         mPidSet.clear();
192         mVctProgramNumberSet.clear();
193         mVctCaptionTracksFound.clear();
194         mEitCaptionTracksFound.clear();
195         mChannelMap.clear();
196     }
197 
startDetecting(int frequency, String modulation)198     public void startDetecting(int frequency, String modulation) {
199         reset();
200         mFrequency = frequency;
201         mModulation = modulation;
202     }
203 
startListening(int pid)204     private void startListening(int pid) {
205         if (mPidSet.contains(pid)) {
206             return;
207         }
208         mPidSet.add(pid);
209         mTunerHal.addPidFilter(pid, TunerHal.FILTER_TYPE_OTHER);
210     }
211 
feedTSStream(byte[] data, int startOffset, int length)212     public void feedTSStream(byte[] data, int startOffset, int length) {
213         if (mPidSet.isEmpty()) {
214             startListening(TsParser.ATSC_SI_BASE_PID);
215         }
216         if (mTsParser != null) {
217             mTsParser.feedTSData(data, startOffset, length);
218         }
219     }
220 
getIncompleteChannels()221     public List<TunerChannel> getIncompleteChannels() {
222         return mTsParser.getIncompleteChannels();
223     }
224 }
225