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