• 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.tv.tuner.tvinput;
18 
19 import android.util.Log;
20 import android.util.SparseArray;
21 import android.util.SparseBooleanArray;
22 
23 import com.android.tv.tuner.data.PsiData.PatItem;
24 import com.android.tv.tuner.data.PsiData.PmtItem;
25 import com.android.tv.tuner.data.PsipData.EitItem;
26 import com.android.tv.tuner.data.PsipData.VctItem;
27 import com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
28 import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
29 import com.android.tv.tuner.data.TunerChannel;
30 import com.android.tv.tuner.source.FileTsStreamer;
31 import com.android.tv.tuner.ts.TsParser;
32 import com.android.tv.tuner.tvinput.EventDetector.EventListener;
33 
34 import java.util.ArrayList;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Set;
38 
39 /**
40  * PSIP event detector for a file source.
41  *
42  * <p>Uses {@link TsParser} to analyze input MPEG-2 transport stream, detects and reports
43  * various PSIP-related events via {@link TsParser.TsOutputListener}.
44  */
45 public class FileSourceEventDetector {
46     private static final String TAG = "FileSourceEventDetector";
47     private static final boolean DEBUG = true;
48     public static final int ALL_PROGRAM_NUMBERS = 0;
49 
50     private TsParser mTsParser;
51     private final Set<Integer> mVctProgramNumberSet = new HashSet<>();
52     private final SparseArray<TunerChannel> mChannelMap = new SparseArray<>();
53     private final SparseBooleanArray mVctCaptionTracksFound = new SparseBooleanArray();
54     private final SparseBooleanArray mEitCaptionTracksFound = new SparseBooleanArray();
55     private final EventListener mEventListener;
56     private FileTsStreamer.StreamProvider mStreamProvider;
57     private int mProgramNumber = ALL_PROGRAM_NUMBERS;
58 
FileSourceEventDetector(EventDetector.EventListener listener)59     public FileSourceEventDetector(EventDetector.EventListener listener) {
60         mEventListener = listener;
61     }
62 
63     /**
64      * Starts detecting channel and program information.
65      *
66      * @param provider MPEG-2 transport stream source.
67      * @param programNumber The program number if this is for handling tune request. For scanning
68      *            purpose, supply {@link #ALL_PROGRAM_NUMBERS}.
69      */
start(FileTsStreamer.StreamProvider provider, int programNumber)70     public void start(FileTsStreamer.StreamProvider provider, int programNumber) {
71         mStreamProvider = provider;
72         mProgramNumber = programNumber;
73         reset();
74     }
75 
reset()76     private void reset() {
77         mTsParser = new TsParser(mTsOutputListener); // TODO: Use TsParser.reset()
78         mStreamProvider.clearPidFilter();
79         mVctProgramNumberSet.clear();
80         mVctCaptionTracksFound.clear();
81         mEitCaptionTracksFound.clear();
82         mChannelMap.clear();
83     }
84 
feedTSStream(byte[] data, int startOffset, int length)85     public void feedTSStream(byte[] data, int startOffset, int length) {
86         if (mStreamProvider.isFilterEmpty()) {
87             startListening(TsParser.ATSC_SI_BASE_PID);
88             startListening(TsParser.PAT_PID);
89         }
90         if (mTsParser != null) {
91             mTsParser.feedTSData(data, startOffset, length);
92         }
93     }
94 
startListening(int pid)95     private void startListening(int pid) {
96         if (mStreamProvider.isInFilter(pid)) {
97             return;
98         }
99         mStreamProvider.addPidFilter(pid);
100     }
101 
102     private final TsParser.TsOutputListener mTsOutputListener = new TsParser.TsOutputListener() {
103         @Override
104         public void onPatDetected(List<PatItem> items) {
105             for (PatItem i : items) {
106                 if (mProgramNumber == ALL_PROGRAM_NUMBERS || mProgramNumber == i.getProgramNo()) {
107                     mStreamProvider.addPidFilter(i.getPmtPid());
108                 }
109             }
110         }
111 
112         @Override
113         public void onEitPidDetected(int pid) {
114             startListening(pid);
115         }
116 
117         @Override
118         public void onEitItemParsed(VctItem channel, List<EitItem> items) {
119             TunerChannel tunerChannel = mChannelMap.get(channel.getProgramNumber());
120             if (DEBUG) {
121                 Log.d(TAG, "onEitItemParsed tunerChannel:" + tunerChannel + " "
122                         + channel.getProgramNumber());
123             }
124             int channelSourceId = channel.getSourceId();
125 
126             // Source id 0 is useful for cases where a cable operator wishes to define a channel for
127             // which no EPG data is currently available.
128             // We don't handle such a case.
129             if (channelSourceId == 0) {
130                 return;
131             }
132 
133             // If at least a one caption track have been found in EIT items for the given channel,
134             // we starts to interpret the zero tracks as a clearance of the caption tracks.
135             boolean captionTracksFound = mEitCaptionTracksFound.get(channelSourceId);
136             for (EitItem item : items) {
137                 if (captionTracksFound) {
138                     break;
139                 }
140                 List<AtscCaptionTrack> captionTracks = item.getCaptionTracks();
141                 if (captionTracks != null && !captionTracks.isEmpty()) {
142                     captionTracksFound = true;
143                 }
144             }
145             mEitCaptionTracksFound.put(channelSourceId, captionTracksFound);
146             if (captionTracksFound) {
147                 for (EitItem item : items) {
148                     item.setHasCaptionTrack();
149                 }
150             }
151             if (tunerChannel != null && mEventListener != null) {
152                 mEventListener.onEventDetected(tunerChannel, items);
153             }
154         }
155 
156         @Override
157         public void onEttPidDetected(int pid) {
158             startListening(pid);
159         }
160 
161         @Override
162         public void onAllVctItemsParsed() {
163             // do nothing.
164         }
165 
166         @Override
167         public void onVctItemParsed(VctItem channel, List<PmtItem> pmtItems) {
168             if (DEBUG) {
169                 Log.d(TAG, "onVctItemParsed VCT " + channel);
170                 Log.d(TAG, "                PMT " + pmtItems);
171             }
172 
173             // Merges the audio and caption tracks located in PMT items into the tracks of the given
174             // tuner channel.
175             TunerChannel tunerChannel = TunerChannel.forFile(channel, pmtItems);
176             List<AtscAudioTrack> audioTracks = new ArrayList<>();
177             List<AtscCaptionTrack> captionTracks = new ArrayList<>();
178             for (PmtItem pmtItem : pmtItems) {
179                 if (pmtItem.getAudioTracks() != null) {
180                     audioTracks.addAll(pmtItem.getAudioTracks());
181                 }
182                 if (pmtItem.getCaptionTracks() != null) {
183                     captionTracks.addAll(pmtItem.getCaptionTracks());
184                 }
185             }
186             int channelProgramNumber = channel.getProgramNumber();
187 
188             // If at least a one caption track have been found in VCT items for the given channel,
189             // we starts to interpret the zero tracks as a clearance of the caption tracks.
190             boolean captionTracksFound = mVctCaptionTracksFound.get(channelProgramNumber)
191                     || !captionTracks.isEmpty();
192             mVctCaptionTracksFound.put(channelProgramNumber, captionTracksFound);
193             if (captionTracksFound) {
194                 tunerChannel.setHasCaptionTrack();
195             }
196             tunerChannel.setFilepath(mStreamProvider.getFilepath());
197             tunerChannel.setAudioTracks(audioTracks);
198             tunerChannel.setCaptionTracks(captionTracks);
199 
200             mChannelMap.put(tunerChannel.getProgramNumber(), tunerChannel);
201             boolean found = mVctProgramNumberSet.contains(channelProgramNumber);
202             if (!found) {
203                 mVctProgramNumberSet.add(channelProgramNumber);
204             }
205             if (mEventListener != null) {
206                 mEventListener.onChannelDetected(tunerChannel, !found);
207             }
208         }
209     };
210 }
211