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 171abddd9f6225298066094e20a6c29061b6af4590Nick Chalkopackage com.android.usbtuner.ts; 181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 191abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.Log; 20ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.util.SparseArray; 211abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.SparseBooleanArray; 221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 231abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.usbtuner.data.PsiData.PatItem; 241abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.usbtuner.data.PsiData.PmtItem; 251abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.usbtuner.data.PsipData.EitItem; 261abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.usbtuner.data.PsipData.EttItem; 271abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.usbtuner.data.PsipData.MgtItem; 281abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.usbtuner.data.PsipData.VctItem; 291abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.usbtuner.data.TunerChannel; 301abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.usbtuner.ts.SectionParser.OutputListener; 311abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.usbtuner.util.ByteArrayBuffer; 321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 331abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.ArrayList; 341abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.Arrays; 351abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.HashMap; 361abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.List; 371abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.Map; 381abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.TreeSet; 391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/** 411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Parses MPEG-2 TS packets. 421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */ 431abddd9f6225298066094e20a6c29061b6af4590Nick Chalkopublic class TsParser { 441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final String TAG = "TsParser"; 451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static boolean DEBUG = false; 461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int ATSC_SI_BASE_PID = 0x1ffb; 481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int PAT_PID = 0x0000; 491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int TS_PACKET_START_CODE = 0x47; 501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int TS_PACKET_TEI_MASK = 0x80; 511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int TS_PACKET_SIZE = 188; 521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 53ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko /* 54ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * Using a SparseArray removes the need to auto box the int key for mStreamMap 55ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * in feedTdPacket which is called 100 times a second. This greatly reduces the 56ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * number of objects created and the frequency of garbage collection. 57ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * Other maps might be suitable for a SparseArray, but the performance 58ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * trade offs must be considered carefully. 59ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * mStreamMap is the only one called at such a high rate. 60ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko */ 61ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private final SparseArray<Stream> mStreamMap = new SparseArray<>(); 621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final Map<Integer, VctItem> mSourceIdToVctItemMap = new HashMap<>(); 631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final Map<Integer, String> mSourceIdToVctItemDescriptionMap = new HashMap<>(); 641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final Map<Integer, VctItem> mProgramNumberToVctItemMap = new HashMap<>(); 651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final Map<Integer, List<PmtItem>> mProgramNumberToPMTMap = new HashMap<>(); 661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final Map<Integer, List<EitItem>> mSourceIdToEitMap = new HashMap<>(); 671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final Map<EventSourceEntry, List<EitItem>> mEitMap = new HashMap<>(); 681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final Map<EventSourceEntry, List<EttItem>> mETTMap = new HashMap<>(); 691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final TreeSet<Integer> mEITPids = new TreeSet<>(); 701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final TreeSet<Integer> mETTPids = new TreeSet<>(); 711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final SparseBooleanArray mProgramNumberHandledStatus = new SparseBooleanArray(); 721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final SparseBooleanArray mVctItemHandledStatus = new SparseBooleanArray(); 731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private TsOutputListener mListener; 741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private int mPartialTSPacketSize; 761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private byte[] mPartialTSPacketBuf = new byte[TS_PACKET_SIZE]; 771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public interface TsOutputListener { 791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko void onPatDetected(List<PatItem> items); 801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko void onEitPidDetected(int pid); 811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko void onVctItemParsed(VctItem channel, List<PmtItem> pmtItems); 821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko void onEitItemParsed(VctItem channel, List<EitItem> items); 831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko void onEttPidDetected(int pid); 841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private abstract class Stream { 871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int INVALID_CONTINUITY_COUNTER = -1; 881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int NUM_CONTINUITY_COUNTER = 16; 891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko protected int mContinuityCounter = INVALID_CONTINUITY_COUNTER; 911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko protected final ByteArrayBuffer mPacket = new ByteArrayBuffer(TS_PACKET_SIZE); 921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void feedData(byte[] data, int continuityCounter, boolean startIndicator) { 941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if ((mContinuityCounter + 1) % NUM_CONTINUITY_COUNTER != continuityCounter) { 951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mPacket.setLength(0); 961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mContinuityCounter = continuityCounter; 981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko handleData(data, startIndicator); 991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko protected abstract void handleData(byte[] data, boolean startIndicator); 1021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private class SectionStream extends Stream { 1051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final SectionParser mSectionParser; 1061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final int mPid; 1071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public SectionStream(int pid) { 1091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mPid = pid; 1101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mSectionParser = new SectionParser(mSectionListener); 1111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko protected void handleData(byte[] data, boolean startIndicator) { 1151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int startPos = 0; 1161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mPacket.length() == 0) { 1171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (startIndicator) { 1181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko startPos = (data[0] & 0xff) + 1; 1191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else { 1201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Don't know where the section starts yet. Wait until start indicator is on. 1211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return; 1221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else { 1241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (startIndicator) { 1251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko startPos = 1; 1261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // When a broken packet is encountered, parsing will stop and return right away. 1301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (startPos >= data.length) { 1311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mPacket.setLength(0); 1321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return; 1331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mPacket.append(data, startPos, data.length - startPos); 1351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mSectionParser.parseSections(mPacket); 1361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private OutputListener mSectionListener = new OutputListener() { 1391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void onPatParsed(List<PatItem> items) { 1411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko for (PatItem i : items) { 1421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko startListening(i.getPmtPid()); 1431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mListener != null) { 1451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mListener.onPatDetected(items); 1461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void onPmtParsed(int programNumber, List<PmtItem> items) { 1511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mProgramNumberToPMTMap.put(programNumber, items); 1521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) { 1531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.d(TAG, "onPMTParsed, programNo " + programNumber + " handledStatus is " 1541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko + mProgramNumberHandledStatus.get(programNumber, false)); 1551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int statusIndex = mProgramNumberHandledStatus.indexOfKey(programNumber); 1571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (statusIndex < 0) { 1581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mProgramNumberHandledStatus.put(programNumber, false); 1591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return; 1601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (!mProgramNumberHandledStatus.valueAt(statusIndex)) { 1621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko VctItem vctItem = mProgramNumberToVctItemMap.get(programNumber); 1631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (vctItem != null) { 1641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // When PMT is parsed later than VCT. 1651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mProgramNumberHandledStatus.put(programNumber, true); 1661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko handleVctItem(vctItem, items); 1671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void onMgtParsed(List<MgtItem> items) { 1731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko for (MgtItem i : items) { 1741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mStreamMap.get(i.getTableTypePid()) != null) { 1751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko continue; 1761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (i.getTableType() >= MgtItem.TABLE_TYPE_EIT_RANGE_START 1781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && i.getTableType() <= MgtItem.TABLE_TYPE_EIT_RANGE_END) { 1791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko startListening(i.getTableTypePid()); 1801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mEITPids.add(i.getTableTypePid()); 1811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mListener != null) { 1821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mListener.onEitPidDetected(i.getTableTypePid()); 1831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else if (i.getTableType() == MgtItem.TABLE_TYPE_CHANNEL_ETT || 1851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko (i.getTableType() >= MgtItem.TABLE_TYPE_ETT_RANGE_START 1861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && i.getTableType() <= MgtItem.TABLE_TYPE_ETT_RANGE_END)) { 1871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko startListening(i.getTableTypePid()); 1881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mETTPids.add(i.getTableTypePid()); 1891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mListener != null) { 1901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mListener.onEttPidDetected(i.getTableTypePid()); 1911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void onVctParsed(List<VctItem> items) { 1981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko for (VctItem i : items) { 199ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (DEBUG) Log.d(TAG, "onVCTParsed " + i); 2001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (i.getSourceId() != 0) { 2011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mSourceIdToVctItemMap.put(i.getSourceId(), i); 2021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko i.setDescription(mSourceIdToVctItemDescriptionMap.get(i.getSourceId())); 2031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int programNumber = i.getProgramNumber(); 2051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mProgramNumberToVctItemMap.put(programNumber, i); 2061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko List<PmtItem> pmtList = mProgramNumberToPMTMap.get(programNumber); 2071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (pmtList != null) { 2081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mProgramNumberHandledStatus.put(programNumber, true); 2091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko handleVctItem(i, pmtList); 2101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else { 2111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mProgramNumberHandledStatus.put(programNumber, false); 2121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.i(TAG, "onVCTParsed, but PMT for programNo " + programNumber 2131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko + " is not found yet."); 2141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 2191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void onEitParsed(int sourceId, List<EitItem> items) { 220ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (DEBUG) Log.d(TAG, "onEITParsed " + sourceId); 2211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko EventSourceEntry entry = new EventSourceEntry(mPid, sourceId); 2221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mEitMap.put(entry, items); 2231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko handleEvents(sourceId); 2241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 2271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void onEttParsed(int sourceId, List<EttItem> descriptions) { 2281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) { 2291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.d(TAG, String.format("onETTParsed sourceId: %d, descriptions.size(): %d", 2301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko sourceId, descriptions.size())); 2311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko for (EttItem item : descriptions) { 2331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (item.eventId == 0) { 2341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Channel description 2351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mSourceIdToVctItemDescriptionMap.put(sourceId, item.text); 2361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko VctItem vctItem = mSourceIdToVctItemMap.get(sourceId); 2371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (vctItem != null) { 2381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko vctItem.setDescription(item.text); 2391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko List<PmtItem> pmtItems = 2401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mProgramNumberToPMTMap.get(vctItem.getProgramNumber()); 2411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (pmtItems != null) { 2421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko handleVctItem(vctItem, pmtItems); 2431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Event Information description 2491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko EventSourceEntry entry = new EventSourceEntry(mPid, sourceId); 2501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mETTMap.put(entry, descriptions); 2511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko handleEvents(sourceId); 2521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko }; 2541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static class EventSourceEntry { 2571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public final int pid; 2581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public final int sourceId; 2591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public EventSourceEntry(int pid, int sourceId) { 2611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko this.pid = pid; 2621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko this.sourceId = sourceId; 2631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 2661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public int hashCode() { 2671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int result = 17; 2681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko result = 31 * result + pid; 2691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko result = 31 * result + sourceId; 2701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return result; 2711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 2741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public boolean equals(Object obj) { 2751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (obj instanceof EventSourceEntry) { 2761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko EventSourceEntry another = (EventSourceEntry) obj; 2771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return pid == another.pid && sourceId == another.sourceId; 2781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return false; 2801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private void handleVctItem(VctItem channel, List<PmtItem> pmtItems) { 2841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mListener != null) { 2851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mListener.onVctItemParsed(channel, pmtItems); 2861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int sourceId = channel.getSourceId(); 2881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int statusIndex = mVctItemHandledStatus.indexOfKey(sourceId); 2891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (statusIndex < 0) { 2901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mVctItemHandledStatus.put(sourceId, false); 2911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return; 2921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (!mVctItemHandledStatus.valueAt(statusIndex)) { 2941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko List<EitItem> eitItems = mSourceIdToEitMap.get(sourceId); 2951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (eitItems != null) { 2961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // When VCT is parsed later than EIT. 2971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mVctItemHandledStatus.put(sourceId, true); 2981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko handleEitItems(channel, eitItems); 2991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 3031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private void handleEitItems(VctItem channel, List<EitItem> items) { 3041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mListener != null) { 3051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mListener.onEitItemParsed(channel, items); 3061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 3091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private void handleEvents(int sourceId) { 3101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Map<Integer, EitItem> itemSet = new HashMap<>(); 3111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko for (int pid : mEITPids) { 3121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko List<EitItem> eitItems = mEitMap.get(new EventSourceEntry(pid, sourceId)); 3131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (eitItems != null) { 3141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko for (EitItem item : eitItems) { 3151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko item.setDescription(null); 3161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko itemSet.put(item.getEventId(), item); 3171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko for (int pid : mETTPids) { 3211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko List<EttItem> ettItems = mETTMap.get(new EventSourceEntry(pid, sourceId)); 3221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (ettItems != null) { 3231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko for (EttItem ettItem : ettItems) { 3241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (ettItem.eventId != 0) { 3251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko EitItem item = itemSet.get(ettItem.eventId); 3261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (item != null) { 3271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko item.setDescription(ettItem.text); 3281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko List<EitItem> items = new ArrayList<>(itemSet.values()); 3341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mSourceIdToEitMap.put(sourceId, items); 3351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko VctItem channel = mSourceIdToVctItemMap.get(sourceId); 3361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (channel != null && mProgramNumberHandledStatus.get(channel.getProgramNumber())) { 3371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mVctItemHandledStatus.put(sourceId, true); 3381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko handleEitItems(channel, items); 3391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else { 3401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mVctItemHandledStatus.put(sourceId, false); 3411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.i(TAG, "onEITParsed, but VCT for sourceId " + sourceId + " is not found yet."); 3421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 3451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public TsParser(TsOutputListener listener) { 3461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko startListening(ATSC_SI_BASE_PID); 3471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko startListening(PAT_PID); 3481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mListener = listener; 3491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 3511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private void startListening(int pid) { 3521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mStreamMap.put(pid, new SectionStream(pid)); 3531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 3551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private boolean feedTSPacket(byte[] tsData, int pos) { 3561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (tsData.length < pos + TS_PACKET_SIZE) { 357ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (DEBUG) Log.d(TAG, "Data should include a single TS packet."); 3581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return false; 3591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (tsData[pos] != TS_PACKET_START_CODE) { 361ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (DEBUG) Log.d(TAG, "Invalid ts packet."); 3621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return false; 3631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if ((tsData[pos + 1] & TS_PACKET_TEI_MASK) != 0) { 365ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (DEBUG) Log.d(TAG, "Erroneous ts packet."); 3661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return false; 3671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 3691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // For details for the structure of TS packet, see H.222.0 Table 2-2. 3701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int pid = ((tsData[pos + 1] & 0x1f) << 8) | (tsData[pos + 2] & 0xff); 3711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko boolean hasAdaptation = (tsData[pos + 3] & 0x20) != 0; 3721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko boolean hasPayload = (tsData[pos + 3] & 0x10) != 0; 3731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko boolean payloadStartIndicator = (tsData[pos + 1] & 0x40) != 0; 3741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int continuityCounter = tsData[pos + 3] & 0x0f; 3751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Stream stream = mStreamMap.get(pid); 3761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int payloadPos = pos; 3771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko payloadPos += hasAdaptation ? 5 + (tsData[pos + 4] & 0xff) : 4; 3781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (!hasPayload || stream == null) { 3791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // We are not interested in this packet. 3801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return false; 3811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (payloadPos > pos + TS_PACKET_SIZE) { 383ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (DEBUG) Log.d(TAG, "Payload should be included in a single TS packet."); 3841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return false; 3851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko stream.feedData(Arrays.copyOfRange(tsData, payloadPos, pos + TS_PACKET_SIZE), 3871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko continuityCounter, payloadStartIndicator); 3881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return true; 3891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 3911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void feedTSData(byte[] tsData, int pos, int length) { 3921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int origPos = pos; 3931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mPartialTSPacketSize != 0 3941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && (mPartialTSPacketSize + length) > TS_PACKET_SIZE) { 3951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko System.arraycopy(tsData, pos, mPartialTSPacketBuf, mPartialTSPacketSize, 3961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko TS_PACKET_SIZE - mPartialTSPacketSize); 3971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko feedTSPacket(mPartialTSPacketBuf, 0); 3981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos += TS_PACKET_SIZE - mPartialTSPacketSize; 3991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mPartialTSPacketSize = 0; 4001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko for (; pos <= length - TS_PACKET_SIZE; pos += TS_PACKET_SIZE) { 4021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko feedTSPacket(tsData, pos); 4031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int remaining = origPos + length - pos; 4051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (remaining > 0) { 4061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko System.arraycopy(tsData, pos, mPartialTSPacketBuf, mPartialTSPacketSize, remaining); 4071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 4101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public List<TunerChannel> getIncompleteChannels() { 4111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko List<TunerChannel> incompleteChannels = new ArrayList<>(); 4121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko for (int i = 0; i < mProgramNumberHandledStatus.size(); i++) { 4131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (!mProgramNumberHandledStatus.valueAt(i)) { 4141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int programNumber = mProgramNumberHandledStatus.keyAt(i); 4151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko List<PmtItem> pmtList = mProgramNumberToPMTMap.get(programNumber); 4161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (pmtList != null) { 4171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko TunerChannel tunerChannel = new TunerChannel(programNumber, pmtList); 4181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko incompleteChannels.add(tunerChannel); 4191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return incompleteChannels; 4231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko} 425