aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/tv/tuner/data
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/tv/tuner/data')
-rw-r--r--src/com/android/tv/tuner/data/Cea708Data.java329
-rw-r--r--src/com/android/tv/tuner/data/PsiData.java93
-rw-r--r--src/com/android/tv/tuner/data/PsipData.java871
-rw-r--r--src/com/android/tv/tuner/data/TunerChannel.java517
4 files changed, 1810 insertions, 0 deletions
diff --git a/src/com/android/tv/tuner/data/Cea708Data.java b/src/com/android/tv/tuner/data/Cea708Data.java
new file mode 100644
index 00000000..73a90181
--- /dev/null
+++ b/src/com/android/tv/tuner/data/Cea708Data.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.tuner.data;
+
+import android.graphics.Color;
+import android.support.annotation.NonNull;
+import com.android.tv.tuner.cc.Cea708Parser;
+
+/** Collection of CEA-708 structures. */
+public class Cea708Data {
+
+ private Cea708Data() {}
+
+ // According to CEA-708B, the range of valid service number is between 1 and 63.
+ public static final int EMPTY_SERVICE_NUMBER = 0;
+
+ // For the details of the ranges of DTVCC code groups, see CEA-708B Table 6.
+ public static final int CODE_C0_RANGE_START = 0x00;
+ public static final int CODE_C0_RANGE_END = 0x1f;
+ public static final int CODE_C1_RANGE_START = 0x80;
+ public static final int CODE_C1_RANGE_END = 0x9f;
+ public static final int CODE_G0_RANGE_START = 0x20;
+ public static final int CODE_G0_RANGE_END = 0x7f;
+ public static final int CODE_G1_RANGE_START = 0xa0;
+ public static final int CODE_G1_RANGE_END = 0xff;
+ public static final int CODE_C2_RANGE_START = 0x00;
+ public static final int CODE_C2_RANGE_END = 0x1f;
+ public static final int CODE_C3_RANGE_START = 0x80;
+ public static final int CODE_C3_RANGE_END = 0x9f;
+ public static final int CODE_G2_RANGE_START = 0x20;
+ public static final int CODE_G2_RANGE_END = 0x7f;
+ public static final int CODE_G3_RANGE_START = 0xa0;
+ public static final int CODE_G3_RANGE_END = 0xff;
+
+ // The following ranges are defined in CEA-708B Section 7.4.1.
+ public static final int CODE_C0_SKIP2_RANGE_START = 0x18;
+ public static final int CODE_C0_SKIP2_RANGE_END = 0x1f;
+ public static final int CODE_C0_SKIP1_RANGE_START = 0x10;
+ public static final int CODE_C0_SKIP1_RANGE_END = 0x17;
+
+ // The following ranges are defined in CEA-708B Section 7.4.7.
+ public static final int CODE_C2_SKIP0_RANGE_START = 0x00;
+ public static final int CODE_C2_SKIP0_RANGE_END = 0x07;
+ public static final int CODE_C2_SKIP1_RANGE_START = 0x08;
+ public static final int CODE_C2_SKIP1_RANGE_END = 0x0f;
+ public static final int CODE_C2_SKIP2_RANGE_START = 0x10;
+ public static final int CODE_C2_SKIP2_RANGE_END = 0x17;
+ public static final int CODE_C2_SKIP3_RANGE_START = 0x18;
+ public static final int CODE_C2_SKIP3_RANGE_END = 0x1f;
+
+ // The following ranges are defined in CEA-708B Section 7.4.8.
+ public static final int CODE_C3_SKIP4_RANGE_START = 0x80;
+ public static final int CODE_C3_SKIP4_RANGE_END = 0x87;
+ public static final int CODE_C3_SKIP5_RANGE_START = 0x88;
+ public static final int CODE_C3_SKIP5_RANGE_END = 0x8f;
+
+ // The following values are the special characters of CEA-708 spec.
+ public static final int CODE_C0_NUL = 0x00;
+ public static final int CODE_C0_ETX = 0x03;
+ public static final int CODE_C0_BS = 0x08;
+ public static final int CODE_C0_FF = 0x0c;
+ public static final int CODE_C0_CR = 0x0d;
+ public static final int CODE_C0_HCR = 0x0e;
+ public static final int CODE_C0_EXT1 = 0x10;
+ public static final int CODE_C0_P16 = 0x18;
+ public static final int CODE_G0_MUSICNOTE = 0x7f;
+ public static final int CODE_G2_TSP = 0x20;
+ public static final int CODE_G2_NBTSP = 0x21;
+ public static final int CODE_G2_BLK = 0x30;
+ public static final int CODE_G3_CC = 0xa0;
+
+ // The following values are the command bits of CEA-708 spec.
+ public static final int CODE_C1_CW0 = 0x80;
+ public static final int CODE_C1_CW1 = 0x81;
+ public static final int CODE_C1_CW2 = 0x82;
+ public static final int CODE_C1_CW3 = 0x83;
+ public static final int CODE_C1_CW4 = 0x84;
+ public static final int CODE_C1_CW5 = 0x85;
+ public static final int CODE_C1_CW6 = 0x86;
+ public static final int CODE_C1_CW7 = 0x87;
+ public static final int CODE_C1_CLW = 0x88;
+ public static final int CODE_C1_DSW = 0x89;
+ public static final int CODE_C1_HDW = 0x8a;
+ public static final int CODE_C1_TGW = 0x8b;
+ public static final int CODE_C1_DLW = 0x8c;
+ public static final int CODE_C1_DLY = 0x8d;
+ public static final int CODE_C1_DLC = 0x8e;
+ public static final int CODE_C1_RST = 0x8f;
+ public static final int CODE_C1_SPA = 0x90;
+ public static final int CODE_C1_SPC = 0x91;
+ public static final int CODE_C1_SPL = 0x92;
+ public static final int CODE_C1_SWA = 0x97;
+ public static final int CODE_C1_DF0 = 0x98;
+ public static final int CODE_C1_DF1 = 0x99;
+ public static final int CODE_C1_DF2 = 0x9a;
+ public static final int CODE_C1_DF3 = 0x9b;
+ public static final int CODE_C1_DF4 = 0x9c;
+ public static final int CODE_C1_DF5 = 0x9d;
+ public static final int CODE_C1_DF6 = 0x9e;
+ public static final int CODE_C1_DF7 = 0x9f;
+
+ public static class CcPacket implements Comparable<CcPacket> {
+ public final byte[] bytes;
+ public final int ccCount;
+ public final long pts;
+
+ public CcPacket(byte[] bytes, int ccCount, long pts) {
+ this.bytes = bytes;
+ this.ccCount = ccCount;
+ this.pts = pts;
+ }
+
+ @Override
+ public int compareTo(@NonNull CcPacket another) {
+ return Long.compare(pts, another.pts);
+ }
+ }
+
+ /** CEA-708B-specific color. */
+ public static class CaptionColor {
+ public static final int OPACITY_SOLID = 0;
+ public static final int OPACITY_FLASH = 1;
+ public static final int OPACITY_TRANSLUCENT = 2;
+ public static final int OPACITY_TRANSPARENT = 3;
+
+ private static final int[] COLOR_MAP = new int[] {0x00, 0x0f, 0xf0, 0xff};
+ private static final int[] OPACITY_MAP = new int[] {0xff, 0xfe, 0x80, 0x00};
+
+ public final int opacity;
+ public final int red;
+ public final int green;
+ public final int blue;
+
+ public CaptionColor(int opacity, int red, int green, int blue) {
+ this.opacity = opacity;
+ this.red = red;
+ this.green = green;
+ this.blue = blue;
+ }
+
+ public int getArgbValue() {
+ return Color.argb(
+ OPACITY_MAP[opacity], COLOR_MAP[red], COLOR_MAP[green], COLOR_MAP[blue]);
+ }
+ }
+
+ /** Caption event generated by {@link Cea708Parser}. */
+ public static class CaptionEvent {
+ @Cea708Parser.CaptionEmitType public final int type;
+ public final Object obj;
+
+ public CaptionEvent(int type, Object obj) {
+ this.type = type;
+ this.obj = obj;
+ }
+ }
+
+ /** Pen style information. */
+ public static class CaptionPenAttr {
+ // Pen sizes
+ public static final int PEN_SIZE_SMALL = 0;
+ public static final int PEN_SIZE_STANDARD = 1;
+ public static final int PEN_SIZE_LARGE = 2;
+
+ // Offsets
+ public static final int OFFSET_SUBSCRIPT = 0;
+ public static final int OFFSET_NORMAL = 1;
+ public static final int OFFSET_SUPERSCRIPT = 2;
+
+ public final int penSize;
+ public final int penOffset;
+ public final int textTag;
+ public final int fontTag;
+ public final int edgeType;
+ public final boolean underline;
+ public final boolean italic;
+
+ public CaptionPenAttr(
+ int penSize,
+ int penOffset,
+ int textTag,
+ int fontTag,
+ int edgeType,
+ boolean underline,
+ boolean italic) {
+ this.penSize = penSize;
+ this.penOffset = penOffset;
+ this.textTag = textTag;
+ this.fontTag = fontTag;
+ this.edgeType = edgeType;
+ this.underline = underline;
+ this.italic = italic;
+ }
+ }
+
+ /**
+ * {@link CaptionColor} objects that indicate the foreground, background, and edge color of a
+ * pen.
+ */
+ public static class CaptionPenColor {
+ public final CaptionColor foregroundColor;
+ public final CaptionColor backgroundColor;
+ public final CaptionColor edgeColor;
+
+ public CaptionPenColor(
+ CaptionColor foregroundColor,
+ CaptionColor backgroundColor,
+ CaptionColor edgeColor) {
+ this.foregroundColor = foregroundColor;
+ this.backgroundColor = backgroundColor;
+ this.edgeColor = edgeColor;
+ }
+ }
+
+ /** Location information of a pen. */
+ public static class CaptionPenLocation {
+ public final int row;
+ public final int column;
+
+ public CaptionPenLocation(int row, int column) {
+ this.row = row;
+ this.column = column;
+ }
+ }
+
+ /** Attributes of a caption window, which is defined in CEA-708B. */
+ public static class CaptionWindowAttr {
+ public static final int JUSTIFY_LEFT = 0;
+ public static final int JUSTIFY_CENTER = 2;
+ public static final int PRINT_LEFT_TO_RIGHT = 0;
+ public static final int PRINT_RIGHT_TO_LEFT = 1;
+ public static final int PRINT_TOP_TO_BOTTOM = 2;
+ public static final int PRINT_BOTTOM_TO_TOP = 3;
+
+ public final CaptionColor fillColor;
+ public final CaptionColor borderColor;
+ public final int borderType;
+ public final boolean wordWrap;
+ public final int printDirection;
+ public final int scrollDirection;
+ public final int justify;
+ public final int effectDirection;
+ public final int effectSpeed;
+ public final int displayEffect;
+
+ public CaptionWindowAttr(
+ CaptionColor fillColor,
+ CaptionColor borderColor,
+ int borderType,
+ boolean wordWrap,
+ int printDirection,
+ int scrollDirection,
+ int justify,
+ int effectDirection,
+ int effectSpeed,
+ int displayEffect) {
+ this.fillColor = fillColor;
+ this.borderColor = borderColor;
+ this.borderType = borderType;
+ this.wordWrap = wordWrap;
+ this.printDirection = printDirection;
+ this.scrollDirection = scrollDirection;
+ this.justify = justify;
+ this.effectDirection = effectDirection;
+ this.effectSpeed = effectSpeed;
+ this.displayEffect = displayEffect;
+ }
+ }
+
+ /** Construction information of the caption window of CEA-708B. */
+ public static class CaptionWindow {
+ public final int id;
+ public final boolean visible;
+ public final boolean rowLock;
+ public final boolean columnLock;
+ public final int priority;
+ public final boolean relativePositioning;
+ public final int anchorVertical;
+ public final int anchorHorizontal;
+ public final int anchorId;
+ public final int rowCount;
+ public final int columnCount;
+ public final int penStyle;
+ public final int windowStyle;
+
+ public CaptionWindow(
+ int id,
+ boolean visible,
+ boolean rowLock,
+ boolean columnLock,
+ int priority,
+ boolean relativePositioning,
+ int anchorVertical,
+ int anchorHorizontal,
+ int anchorId,
+ int rowCount,
+ int columnCount,
+ int penStyle,
+ int windowStyle) {
+ this.id = id;
+ this.visible = visible;
+ this.rowLock = rowLock;
+ this.columnLock = columnLock;
+ this.priority = priority;
+ this.relativePositioning = relativePositioning;
+ this.anchorVertical = anchorVertical;
+ this.anchorHorizontal = anchorHorizontal;
+ this.anchorId = anchorId;
+ this.rowCount = rowCount;
+ this.columnCount = columnCount;
+ this.penStyle = penStyle;
+ this.windowStyle = windowStyle;
+ }
+ }
+}
diff --git a/src/com/android/tv/tuner/data/PsiData.java b/src/com/android/tv/tuner/data/PsiData.java
new file mode 100644
index 00000000..9b7c2e2c
--- /dev/null
+++ b/src/com/android/tv/tuner/data/PsiData.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.tuner.data;
+
+import com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
+import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
+import java.util.List;
+
+/** Collection of MPEG PSI table items. */
+public class PsiData {
+
+ private PsiData() {}
+
+ public static class PatItem {
+ private final int mProgramNo;
+ private final int mPmtPid;
+
+ public PatItem(int programNo, int pmtPid) {
+ mProgramNo = programNo;
+ mPmtPid = pmtPid;
+ }
+
+ public int getProgramNo() {
+ return mProgramNo;
+ }
+
+ public int getPmtPid() {
+ return mPmtPid;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Program No: %x PMT Pid: %x", mProgramNo, mPmtPid);
+ }
+ }
+
+ public static class PmtItem {
+ public static final int ES_PID_PCR = 0x100;
+
+ private final int mStreamType;
+ private final int mEsPid;
+ private final List<AtscAudioTrack> mAudioTracks;
+ private final List<AtscCaptionTrack> mCaptionTracks;
+
+ public PmtItem(
+ int streamType,
+ int esPid,
+ List<AtscAudioTrack> audioTracks,
+ List<AtscCaptionTrack> captionTracks) {
+ mStreamType = streamType;
+ mEsPid = esPid;
+ mAudioTracks = audioTracks;
+ mCaptionTracks = captionTracks;
+ }
+
+ public int getStreamType() {
+ return mStreamType;
+ }
+
+ public int getEsPid() {
+ return mEsPid;
+ }
+
+ public List<AtscAudioTrack> getAudioTracks() {
+ return mAudioTracks;
+ }
+
+ public List<AtscCaptionTrack> getCaptionTracks() {
+ return mCaptionTracks;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "Stream Type: %x ES Pid: %x AudioTracks: %s CaptionTracks: %s",
+ mStreamType, mEsPid, mAudioTracks, mCaptionTracks);
+ }
+ }
+}
diff --git a/src/com/android/tv/tuner/data/PsipData.java b/src/com/android/tv/tuner/data/PsipData.java
new file mode 100644
index 00000000..6459004c
--- /dev/null
+++ b/src/com/android/tv/tuner/data/PsipData.java
@@ -0,0 +1,871 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.tuner.data;
+
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
+import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
+import com.android.tv.tuner.ts.SectionParser;
+import com.android.tv.tuner.util.ConvertUtils;
+import com.android.tv.util.StringUtils;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+
+/** Collection of ATSC PSIP table items. */
+public class PsipData {
+
+ private PsipData() {}
+
+ public static class PsipSection {
+ private final int mTableId;
+ private final int mTableIdExtension;
+ private final int mSectionNumber;
+ private final boolean mCurrentNextIndicator;
+
+ public static PsipSection create(byte[] data) {
+ if (data.length < 9) {
+ return null;
+ }
+ int tableId = data[0] & 0xff;
+ int tableIdExtension = (data[3] & 0xff) << 8 | (data[4] & 0xff);
+ int sectionNumber = data[6] & 0xff;
+ boolean currentNextIndicator = (data[5] & 0x01) != 0;
+ return new PsipSection(tableId, tableIdExtension, sectionNumber, currentNextIndicator);
+ }
+
+ private PsipSection(
+ int tableId,
+ int tableIdExtension,
+ int sectionNumber,
+ boolean currentNextIndicator) {
+ mTableId = tableId;
+ mTableIdExtension = tableIdExtension;
+ mSectionNumber = sectionNumber;
+ mCurrentNextIndicator = currentNextIndicator;
+ }
+
+ public int getTableId() {
+ return mTableId;
+ }
+
+ public int getTableIdExtension() {
+ return mTableIdExtension;
+ }
+
+ public int getSectionNumber() {
+ return mSectionNumber;
+ }
+
+ // This is for indicating that the section sent is applicable.
+ // We only consider a situation where currentNextIndicator is expected to have a true value.
+ // So, we are not going to compare this variable in hashCode() and equals() methods.
+ public boolean getCurrentNextIndicator() {
+ return mCurrentNextIndicator;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + mTableId;
+ result = 31 * result + mTableIdExtension;
+ result = 31 * result + mSectionNumber;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof PsipSection) {
+ PsipSection another = (PsipSection) obj;
+ return mTableId == another.getTableId()
+ && mTableIdExtension == another.getTableIdExtension()
+ && mSectionNumber == another.getSectionNumber();
+ }
+ return false;
+ }
+ }
+
+ /** {@link TvTracksInterface} for serving the audio and caption tracks. */
+ public interface TvTracksInterface {
+ /** Set the flag that tells the caption tracks have been found in this section container. */
+ void setHasCaptionTrack();
+
+ /**
+ * Returns whether or not the caption tracks have been found in this section container. If
+ * true, zero caption track will be interpreted as a clearance of the caption tracks.
+ */
+ boolean hasCaptionTrack();
+
+ /** Returns the audio tracks received. */
+ List<AtscAudioTrack> getAudioTracks();
+
+ /** Returns the caption tracks received. */
+ List<AtscCaptionTrack> getCaptionTracks();
+ }
+
+ public static class MgtItem {
+ public static final int TABLE_TYPE_EIT_RANGE_START = 0x0100;
+ public static final int TABLE_TYPE_EIT_RANGE_END = 0x017f;
+ public static final int TABLE_TYPE_CHANNEL_ETT = 0x0004;
+ public static final int TABLE_TYPE_ETT_RANGE_START = 0x0200;
+ public static final int TABLE_TYPE_ETT_RANGE_END = 0x027f;
+
+ private final int mTableType;
+ private final int mTableTypePid;
+
+ public MgtItem(int tableType, int tableTypePid) {
+ mTableType = tableType;
+ mTableTypePid = tableTypePid;
+ }
+
+ public int getTableType() {
+ return mTableType;
+ }
+
+ public int getTableTypePid() {
+ return mTableTypePid;
+ }
+ }
+
+ public static class VctItem {
+ private final String mShortName;
+ private final String mLongName;
+ private final int mServiceType;
+ private final int mChannelTsid;
+ private final int mProgramNumber;
+ private final int mMajorChannelNumber;
+ private final int mMinorChannelNumber;
+ private final int mSourceId;
+ private String mDescription;
+
+ public VctItem(
+ String shortName,
+ String longName,
+ int serviceType,
+ int channelTsid,
+ int programNumber,
+ int majorChannelNumber,
+ int minorChannelNumber,
+ int sourceId) {
+ mShortName = shortName;
+ mLongName = longName;
+ mServiceType = serviceType;
+ mChannelTsid = channelTsid;
+ mProgramNumber = programNumber;
+ mMajorChannelNumber = majorChannelNumber;
+ mMinorChannelNumber = minorChannelNumber;
+ mSourceId = sourceId;
+ }
+
+ public String getShortName() {
+ return mShortName;
+ }
+
+ public String getLongName() {
+ return mLongName;
+ }
+
+ public int getServiceType() {
+ return mServiceType;
+ }
+
+ public int getChannelTsid() {
+ return mChannelTsid;
+ }
+
+ public int getProgramNumber() {
+ return mProgramNumber;
+ }
+
+ public int getMajorChannelNumber() {
+ return mMajorChannelNumber;
+ }
+
+ public int getMinorChannelNumber() {
+ return mMinorChannelNumber;
+ }
+
+ public int getSourceId() {
+ return mSourceId;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ Locale.US,
+ "ShortName: %s LongName: %s ServiceType: %d ChannelTsid: %x "
+ + "ProgramNumber:%d %d-%d SourceId: %x",
+ mShortName,
+ mLongName,
+ mServiceType,
+ mChannelTsid,
+ mProgramNumber,
+ mMajorChannelNumber,
+ mMinorChannelNumber,
+ mSourceId);
+ }
+
+ public void setDescription(String description) {
+ mDescription = description;
+ }
+
+ public String getDescription() {
+ return mDescription;
+ }
+ }
+
+ public static class SdtItem {
+ private final String mServiceName;
+ private final String mServiceProviderName;
+ private final int mServiceType;
+ private final int mServiceId;
+ private final int mOriginalNetWorkId;
+
+ public SdtItem(
+ String serviceName,
+ String serviceProviderName,
+ int serviceType,
+ int serviceId,
+ int originalNetWorkId) {
+ mServiceName = serviceName;
+ mServiceProviderName = serviceProviderName;
+ mServiceType = serviceType;
+ mServiceId = serviceId;
+ mOriginalNetWorkId = originalNetWorkId;
+ }
+
+ public String getServiceName() {
+ return mServiceName;
+ }
+
+ public String getServiceProviderName() {
+ return mServiceProviderName;
+ }
+
+ public int getServiceType() {
+ return mServiceType;
+ }
+
+ public int getServiceId() {
+ return mServiceId;
+ }
+
+ public int getOriginalNetworkId() {
+ return mOriginalNetWorkId;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "ServiceName: %s ServiceProviderName:%s ServiceType:%d "
+ + "OriginalNetworkId:%d",
+ mServiceName, mServiceProviderName, mServiceType, mOriginalNetWorkId);
+ }
+ }
+
+ /** A base class for descriptors of Ts packets. */
+ public abstract static class TsDescriptor {
+ public abstract int getTag();
+ }
+
+ public static class ContentAdvisoryDescriptor extends TsDescriptor {
+ private final List<RatingRegion> mRatingRegions;
+
+ public ContentAdvisoryDescriptor(List<RatingRegion> ratingRegions) {
+ mRatingRegions = ratingRegions;
+ }
+
+ @Override
+ public int getTag() {
+ return SectionParser.DESCRIPTOR_TAG_CONTENT_ADVISORY;
+ }
+
+ public List<RatingRegion> getRatingRegions() {
+ return mRatingRegions;
+ }
+ }
+
+ public static class CaptionServiceDescriptor extends TsDescriptor {
+ private final List<AtscCaptionTrack> mCaptionTracks;
+
+ public CaptionServiceDescriptor(List<AtscCaptionTrack> captionTracks) {
+ mCaptionTracks = captionTracks;
+ }
+
+ @Override
+ public int getTag() {
+ return SectionParser.DESCRIPTOR_TAG_CAPTION_SERVICE;
+ }
+
+ public List<AtscCaptionTrack> getCaptionTracks() {
+ return mCaptionTracks;
+ }
+ }
+
+ public static class ExtendedChannelNameDescriptor extends TsDescriptor {
+ private final String mLongChannelName;
+
+ public ExtendedChannelNameDescriptor(String longChannelName) {
+ mLongChannelName = longChannelName;
+ }
+
+ @Override
+ public int getTag() {
+ return SectionParser.DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME;
+ }
+
+ public String getLongChannelName() {
+ return mLongChannelName;
+ }
+ }
+
+ public static class GenreDescriptor extends TsDescriptor {
+ private final String[] mBroadcastGenres;
+ private final String[] mCanonicalGenres;
+
+ public GenreDescriptor(String[] broadcastGenres, String[] canonicalGenres) {
+ mBroadcastGenres = broadcastGenres;
+ mCanonicalGenres = canonicalGenres;
+ }
+
+ @Override
+ public int getTag() {
+ return SectionParser.DESCRIPTOR_TAG_GENRE;
+ }
+
+ public String[] getBroadcastGenres() {
+ return mBroadcastGenres;
+ }
+
+ public String[] getCanonicalGenres() {
+ return mCanonicalGenres;
+ }
+ }
+
+ public static class Ac3AudioDescriptor extends TsDescriptor {
+ // See A/52 Annex A. Table A4.2
+ private static final byte SAMPLE_RATE_CODE_48000HZ = 0;
+ private static final byte SAMPLE_RATE_CODE_44100HZ = 1;
+ private static final byte SAMPLE_RATE_CODE_32000HZ = 2;
+
+ private final byte mSampleRateCode;
+ private final byte mBsid;
+ private final byte mBitRateCode;
+ private final byte mSurroundMode;
+ private final byte mBsmod;
+ private final int mNumChannels;
+ private final boolean mFullSvc;
+ private final byte mLangCod;
+ private final byte mLangCod2;
+ private final byte mMainId;
+ private final byte mPriority;
+ private final byte mAsvcflags;
+ private final String mText;
+ private final String mLanguage;
+ private final String mLanguage2;
+
+ public Ac3AudioDescriptor(
+ byte sampleRateCode,
+ byte bsid,
+ byte bitRateCode,
+ byte surroundMode,
+ byte bsmod,
+ int numChannels,
+ boolean fullSvc,
+ byte langCod,
+ byte langCod2,
+ byte mainId,
+ byte priority,
+ byte asvcflags,
+ String text,
+ String language,
+ String language2) {
+ mSampleRateCode = sampleRateCode;
+ mBsid = bsid;
+ mBitRateCode = bitRateCode;
+ mSurroundMode = surroundMode;
+ mBsmod = bsmod;
+ mNumChannels = numChannels;
+ mFullSvc = fullSvc;
+ mLangCod = langCod;
+ mLangCod2 = langCod2;
+ mMainId = mainId;
+ mPriority = priority;
+ mAsvcflags = asvcflags;
+ mText = text;
+ mLanguage = language;
+ mLanguage2 = language2;
+ }
+
+ @Override
+ public int getTag() {
+ return SectionParser.DESCRIPTOR_TAG_AC3_AUDIO_STREAM;
+ }
+
+ public byte getSampleRateCode() {
+ return mSampleRateCode;
+ }
+
+ public int getSampleRate() {
+ switch (mSampleRateCode) {
+ case SAMPLE_RATE_CODE_48000HZ:
+ return 48000;
+ case SAMPLE_RATE_CODE_44100HZ:
+ return 44100;
+ case SAMPLE_RATE_CODE_32000HZ:
+ return 32000;
+ default:
+ return 0;
+ }
+ }
+
+ public byte getBsid() {
+ return mBsid;
+ }
+
+ public byte getBitRateCode() {
+ return mBitRateCode;
+ }
+
+ public byte getSurroundMode() {
+ return mSurroundMode;
+ }
+
+ public byte getBsmod() {
+ return mBsmod;
+ }
+
+ public int getNumChannels() {
+ return mNumChannels;
+ }
+
+ public boolean isFullSvc() {
+ return mFullSvc;
+ }
+
+ public byte getLangCod() {
+ return mLangCod;
+ }
+
+ public byte getLangCod2() {
+ return mLangCod2;
+ }
+
+ public byte getMainId() {
+ return mMainId;
+ }
+
+ public byte getPriority() {
+ return mPriority;
+ }
+
+ public byte getAsvcflags() {
+ return mAsvcflags;
+ }
+
+ public String getText() {
+ return mText;
+ }
+
+ public String getLanguage() {
+ return mLanguage;
+ }
+
+ public String getLanguage2() {
+ return mLanguage2;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ Locale.US,
+ "AC3 audio stream sampleRateCode: %d, bsid: %d, bitRateCode: %d, "
+ + "surroundMode: %d, bsmod: %d, numChannels: %d, fullSvc: %s, langCod: %d, "
+ + "langCod2: %d, mainId: %d, priority: %d, avcflags: %d, text: %s, language: %s"
+ + ", language2: %s",
+ mSampleRateCode,
+ mBsid,
+ mBitRateCode,
+ mSurroundMode,
+ mBsmod,
+ mNumChannels,
+ mFullSvc,
+ mLangCod,
+ mLangCod2,
+ mMainId,
+ mPriority,
+ mAsvcflags,
+ mText,
+ mLanguage,
+ mLanguage2);
+ }
+ }
+
+ public static class Iso639LanguageDescriptor extends TsDescriptor {
+ private final List<AtscAudioTrack> mAudioTracks;
+
+ public Iso639LanguageDescriptor(List<AtscAudioTrack> audioTracks) {
+ mAudioTracks = audioTracks;
+ }
+
+ @Override
+ public int getTag() {
+ return SectionParser.DESCRIPTOR_TAG_ISO639LANGUAGE;
+ }
+
+ public List<AtscAudioTrack> getAudioTracks() {
+ return mAudioTracks;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s %s", getClass().getName(), mAudioTracks);
+ }
+ }
+
+ public static class ServiceDescriptor extends TsDescriptor {
+ private final int mServiceType;
+ private final String mServiceProviderName;
+ private final String mServiceName;
+
+ public ServiceDescriptor(int serviceType, String serviceProviderName, String serviceName) {
+ mServiceType = serviceType;
+ mServiceProviderName = serviceProviderName;
+ mServiceName = serviceName;
+ }
+
+ @Override
+ public int getTag() {
+ return SectionParser.DVB_DESCRIPTOR_TAG_SERVICE;
+ }
+
+ public int getServiceType() {
+ return mServiceType;
+ }
+
+ public String getServiceProviderName() {
+ return mServiceProviderName;
+ }
+
+ public String getServiceName() {
+ return mServiceName;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "Service descriptor, service type: %d, "
+ + "service provider name: %s, "
+ + "service name: %s",
+ mServiceType, mServiceProviderName, mServiceName);
+ }
+ }
+
+ public static class ShortEventDescriptor extends TsDescriptor {
+ private final String mLanguage;
+ private final String mEventName;
+ private final String mText;
+
+ public ShortEventDescriptor(String language, String eventName, String text) {
+ mLanguage = language;
+ mEventName = eventName;
+ mText = text;
+ }
+
+ public String getEventName() {
+ return mEventName;
+ }
+
+ @Override
+ public int getTag() {
+ return SectionParser.DVB_DESCRIPTOR_TAG_SHORT_EVENT;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "ShortEvent Descriptor, language:%s, event name: %s, " + "text:%s",
+ mLanguage, mEventName, mText);
+ }
+ }
+
+ public static class ParentalRatingDescriptor extends TsDescriptor {
+ private final HashMap<String, Integer> mRatings;
+
+ public ParentalRatingDescriptor(HashMap<String, Integer> ratings) {
+ mRatings = ratings;
+ }
+
+ @Override
+ public int getTag() {
+ return SectionParser.DVB_DESCRIPTOR_TAG_PARENTAL_RATING;
+ }
+
+ public HashMap<String, Integer> getRatings() {
+ return mRatings;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Parental rating descriptor, ratings:" + mRatings);
+ }
+ }
+
+ public static class RatingRegion {
+ private final int mName;
+ private final String mDescription;
+ private final List<RegionalRating> mRegionalRatings;
+
+ public RatingRegion(int name, String description, List<RegionalRating> regionalRatings) {
+ mName = name;
+ mDescription = description;
+ mRegionalRatings = regionalRatings;
+ }
+
+ public int getName() {
+ return mName;
+ }
+
+ public String getDescription() {
+ return mDescription;
+ }
+
+ public List<RegionalRating> getRegionalRatings() {
+ return mRegionalRatings;
+ }
+ }
+
+ public static class RegionalRating {
+ private final int mDimension;
+ private final int mRating;
+
+ public RegionalRating(int dimension, int rating) {
+ mDimension = dimension;
+ mRating = rating;
+ }
+
+ public int getDimension() {
+ return mDimension;
+ }
+
+ public int getRating() {
+ return mRating;
+ }
+ }
+
+ public static class EitItem implements Comparable<EitItem>, TvTracksInterface {
+ public static final long INVALID_PROGRAM_ID = -1;
+
+ // A program id is a primary key of TvContract.Programs table. So it must be positive.
+ private final long mProgramId;
+ private final int mEventId;
+ private final String mTitleText;
+ private String mDescription;
+ private final long mStartTime;
+ private final int mLengthInSecond;
+ private final String mContentRating;
+ private final List<AtscAudioTrack> mAudioTracks;
+ private final List<AtscCaptionTrack> mCaptionTracks;
+ private boolean mHasCaptionTrack;
+ private final String mBroadcastGenre;
+ private final String mCanonicalGenre;
+
+ public EitItem(
+ long programId,
+ int eventId,
+ String titleText,
+ long startTime,
+ int lengthInSecond,
+ String contentRating,
+ List<AtscAudioTrack> audioTracks,
+ List<AtscCaptionTrack> captionTracks,
+ String broadcastGenre,
+ String canonicalGenre,
+ String description) {
+ mProgramId = programId;
+ mEventId = eventId;
+ mTitleText = titleText;
+ mStartTime = startTime;
+ mLengthInSecond = lengthInSecond;
+ mContentRating = contentRating;
+ mAudioTracks = audioTracks;
+ mCaptionTracks = captionTracks;
+ mBroadcastGenre = broadcastGenre;
+ mCanonicalGenre = canonicalGenre;
+ mDescription = description;
+ }
+
+ public long getProgramId() {
+ return mProgramId;
+ }
+
+ public int getEventId() {
+ return mEventId;
+ }
+
+ public String getTitleText() {
+ return mTitleText;
+ }
+
+ public void setDescription(String description) {
+ mDescription = description;
+ }
+
+ public String getDescription() {
+ return mDescription;
+ }
+
+ public long getStartTime() {
+ return mStartTime;
+ }
+
+ public int getLengthInSecond() {
+ return mLengthInSecond;
+ }
+
+ public long getStartTimeUtcMillis() {
+ return ConvertUtils.convertGPSTimeToUnixEpoch(mStartTime) * DateUtils.SECOND_IN_MILLIS;
+ }
+
+ public long getEndTimeUtcMillis() {
+ return ConvertUtils.convertGPSTimeToUnixEpoch(mStartTime + mLengthInSecond)
+ * DateUtils.SECOND_IN_MILLIS;
+ }
+
+ public String getContentRating() {
+ return mContentRating;
+ }
+
+ @Override
+ public List<AtscAudioTrack> getAudioTracks() {
+ return mAudioTracks;
+ }
+
+ @Override
+ public List<AtscCaptionTrack> getCaptionTracks() {
+ return mCaptionTracks;
+ }
+
+ public String getBroadcastGenre() {
+ return mBroadcastGenre;
+ }
+
+ public String getCanonicalGenre() {
+ return mCanonicalGenre;
+ }
+
+ @Override
+ public void setHasCaptionTrack() {
+ mHasCaptionTrack = true;
+ }
+
+ @Override
+ public boolean hasCaptionTrack() {
+ return mHasCaptionTrack;
+ }
+
+ @Override
+ public int compareTo(@NonNull EitItem item) {
+ // The list of caption tracks and the program ids are not compared in here because the
+ // channels in TIF have the concept of the caption and audio tracks while the programs
+ // do not and the programs in TIF only have a program id since they are the rows of
+ // Content Provider.
+ int ret = mEventId - item.getEventId();
+ if (ret != 0) {
+ return ret;
+ }
+ ret = StringUtils.compare(mTitleText, item.getTitleText());
+ if (ret != 0) {
+ return ret;
+ }
+ if (mStartTime > item.getStartTime()) {
+ return 1;
+ } else if (mStartTime < item.getStartTime()) {
+ return -1;
+ }
+ if (mLengthInSecond > item.getLengthInSecond()) {
+ return 1;
+ } else if (mLengthInSecond < item.getLengthInSecond()) {
+ return -1;
+ }
+
+ // Compares content ratings
+ ret = StringUtils.compare(mContentRating, item.getContentRating());
+ if (ret != 0) {
+ return ret;
+ }
+
+ // Compares broadcast genres
+ ret = StringUtils.compare(mBroadcastGenre, item.getBroadcastGenre());
+ if (ret != 0) {
+ return ret;
+ }
+ // Compares canonical genres
+ ret = StringUtils.compare(mCanonicalGenre, item.getCanonicalGenre());
+ if (ret != 0) {
+ return ret;
+ }
+
+ // Compares descriptions
+ return StringUtils.compare(mDescription, item.getDescription());
+ }
+
+ public String getAudioLanguage() {
+ if (mAudioTracks == null) {
+ return "";
+ }
+ ArrayList<String> languages = new ArrayList<>();
+ for (AtscAudioTrack audioTrack : mAudioTracks) {
+ languages.add(audioTrack.language);
+ }
+ return TextUtils.join(",", languages);
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ Locale.US,
+ "EitItem programId: %d, eventId: %d, title: %s, startTime: %10d, "
+ + "length: %6d, rating: %s, audio tracks: %d, caption tracks: %d, "
+ + "genres (broadcast: %s, canonical: %s), description: %s",
+ mProgramId,
+ mEventId,
+ mTitleText,
+ mStartTime,
+ mLengthInSecond,
+ mContentRating,
+ mAudioTracks != null ? mAudioTracks.size() : 0,
+ mCaptionTracks != null ? mCaptionTracks.size() : 0,
+ mBroadcastGenre,
+ mCanonicalGenre,
+ mDescription);
+ }
+ }
+
+ public static class EttItem {
+ public final int eventId;
+ public final String text;
+
+ public EttItem(int eventId, String text) {
+ this.eventId = eventId;
+ this.text = text;
+ }
+ }
+}
diff --git a/src/com/android/tv/tuner/data/TunerChannel.java b/src/com/android/tv/tuner/data/TunerChannel.java
new file mode 100644
index 00000000..52356c2e
--- /dev/null
+++ b/src/com/android/tv/tuner/data/TunerChannel.java
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.tuner.data;
+
+import android.support.annotation.NonNull;
+import android.util.Log;
+import com.android.tv.tuner.data.nano.Channel;
+import com.android.tv.tuner.data.nano.Channel.TunerChannelProto;
+import com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
+import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
+import com.android.tv.tuner.util.Ints;
+import com.android.tv.util.StringUtils;
+import com.google.protobuf.nano.MessageNano;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/** A class that represents a single channel accessible through a tuner. */
+public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracksInterface {
+ private static final String TAG = "TunerChannel";
+
+ /** Channel number separator between major number and minor number. */
+ public static final char CHANNEL_NUMBER_SEPARATOR = '-';
+
+ // See ATSC Code Points Registry.
+ private static final String[] ATSC_SERVICE_TYPE_NAMES =
+ new String[] {
+ "ATSC Reserved",
+ "Analog television channels",
+ "ATSC_digital_television",
+ "ATSC_audio",
+ "ATSC_data_only_service",
+ "Software Download",
+ "Unassociated/Small Screen Service",
+ "Parameterized Service",
+ "ATSC NRT Service",
+ "Extended Parameterized Service"
+ };
+ private static final String ATSC_SERVICE_TYPE_NAME_RESERVED =
+ ATSC_SERVICE_TYPE_NAMES[Channel.SERVICE_TYPE_ATSC_RESERVED];
+
+ public static final int INVALID_FREQUENCY = -1;
+
+ // According to RFC4259, The number of available PIDs ranges from 0 to 8191.
+ public static final int INVALID_PID = -1;
+
+ // According to ISO13818-1, Mpeg2 StreamType has a range from 0x00 to 0xff.
+ public static final int INVALID_STREAMTYPE = -1;
+
+ // @GuardedBy(this) Writing operations and toByteArray will be guarded. b/34197766
+ private final TunerChannelProto mProto;
+
+ private TunerChannel(
+ PsipData.VctItem channel, int programNumber, List<PsiData.PmtItem> pmtItems, int type) {
+ mProto = new TunerChannelProto();
+ if (channel == null) {
+ mProto.shortName = "";
+ mProto.tsid = 0;
+ mProto.programNumber = programNumber;
+ mProto.virtualMajor = 0;
+ mProto.virtualMinor = 0;
+ } else {
+ mProto.shortName = channel.getShortName();
+ if (channel.getLongName() != null) {
+ mProto.longName = channel.getLongName();
+ }
+ mProto.tsid = channel.getChannelTsid();
+ mProto.programNumber = channel.getProgramNumber();
+ mProto.virtualMajor = channel.getMajorChannelNumber();
+ mProto.virtualMinor = channel.getMinorChannelNumber();
+ if (channel.getDescription() != null) {
+ mProto.description = channel.getDescription();
+ }
+ mProto.serviceType = channel.getServiceType();
+ }
+ initProto(pmtItems, type);
+ }
+
+ private void initProto(List<PsiData.PmtItem> pmtItems, int type) {
+ mProto.type = type;
+ mProto.channelId = -1L;
+ mProto.frequency = INVALID_FREQUENCY;
+ mProto.videoPid = INVALID_PID;
+ mProto.videoStreamType = INVALID_STREAMTYPE;
+ List<Integer> audioPids = new ArrayList<>();
+ List<Integer> audioStreamTypes = new ArrayList<>();
+ for (PsiData.PmtItem pmt : pmtItems) {
+ switch (pmt.getStreamType()) {
+ // MPEG ES stream video types
+ case Channel.MPEG1:
+ case Channel.MPEG2:
+ case Channel.H263:
+ case Channel.H264:
+ case Channel.H265:
+ mProto.videoPid = pmt.getEsPid();
+ mProto.videoStreamType = pmt.getStreamType();
+ break;
+
+ // MPEG ES stream audio types
+ case Channel.MPEG1AUDIO:
+ case Channel.MPEG2AUDIO:
+ case Channel.MPEG2AACAUDIO:
+ case Channel.MPEG4LATMAACAUDIO:
+ case Channel.A52AC3AUDIO:
+ case Channel.EAC3AUDIO:
+ audioPids.add(pmt.getEsPid());
+ audioStreamTypes.add(pmt.getStreamType());
+ break;
+
+ // Non MPEG ES stream types
+ case 0x100: // PmtItem.ES_PID_PCR:
+ mProto.pcrPid = pmt.getEsPid();
+ break;
+ }
+ }
+ mProto.audioPids = Ints.toArray(audioPids);
+ mProto.audioStreamTypes = Ints.toArray(audioStreamTypes);
+ mProto.audioTrackIndex = (audioPids.size() > 0) ? 0 : -1;
+ }
+
+ private TunerChannel(
+ int programNumber, int type, PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems) {
+ mProto = new TunerChannelProto();
+ mProto.tsid = 0;
+ mProto.virtualMajor = 0;
+ mProto.virtualMinor = 0;
+ if (channel == null) {
+ mProto.shortName = "";
+ mProto.programNumber = programNumber;
+ } else {
+ mProto.shortName = channel.getServiceName();
+ mProto.programNumber = channel.getServiceId();
+ mProto.serviceType = channel.getServiceType();
+ }
+ initProto(pmtItems, type);
+ }
+
+ /** Initialize tuner channel with VCT items and PMT items. */
+ public TunerChannel(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems) {
+ this(channel, 0, pmtItems, Channel.TYPE_TUNER);
+ }
+
+ /** Initialize tuner channel with program number and PMT items. */
+ public TunerChannel(int programNumber, List<PsiData.PmtItem> pmtItems) {
+ this(null, programNumber, pmtItems, Channel.TYPE_TUNER);
+ }
+
+ /** Initialize tuner channel with SDT items and PMT items. */
+ public TunerChannel(PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems) {
+ this(0, Channel.TYPE_TUNER, channel, pmtItems);
+ }
+
+ private TunerChannel(TunerChannelProto tunerChannelProto) {
+ mProto = tunerChannelProto;
+ }
+
+ public static TunerChannel forFile(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems) {
+ return new TunerChannel(channel, 0, pmtItems, Channel.TYPE_FILE);
+ }
+
+ public static TunerChannel forDvbFile(
+ PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems) {
+ return new TunerChannel(0, Channel.TYPE_FILE, channel, pmtItems);
+ }
+
+ /**
+ * Create a TunerChannel object suitable for network tuners
+ *
+ * @param major Channel number major
+ * @param minor Channel number minor
+ * @param programNumber Program number
+ * @param shortName Short name
+ * @param recordingProhibited Recording prohibition info
+ * @param videoFormat Video format. Should be {@code null} or one of the followings: {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_240P}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_360P}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_480I}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_480P}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_576I}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_576P}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_720P}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_1080I}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_1080P}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_2160P}, {@link
+ * android.media.tv.TvContract.Channels#VIDEO_FORMAT_4320P}
+ * @return a TunerChannel object
+ */
+ public static TunerChannel forNetwork(
+ int major,
+ int minor,
+ int programNumber,
+ String shortName,
+ boolean recordingProhibited,
+ String videoFormat) {
+ TunerChannel tunerChannel =
+ new TunerChannel(null, programNumber, Collections.EMPTY_LIST, Channel.TYPE_NETWORK);
+ tunerChannel.setVirtualMajor(major);
+ tunerChannel.setVirtualMinor(minor);
+ tunerChannel.setShortName(shortName);
+ // Set audio and video pids in order to work around the audio-only channel check.
+ tunerChannel.setAudioPids(new ArrayList<>(Arrays.asList(0)));
+ tunerChannel.selectAudioTrack(0);
+ tunerChannel.setVideoPid(0);
+ tunerChannel.setRecordingProhibited(recordingProhibited);
+ if (videoFormat != null) {
+ tunerChannel.setVideoFormat(videoFormat);
+ }
+ return tunerChannel;
+ }
+
+ public String getName() {
+ return (!mProto.shortName.isEmpty()) ? mProto.shortName : mProto.longName;
+ }
+
+ public String getShortName() {
+ return mProto.shortName;
+ }
+
+ public int getProgramNumber() {
+ return mProto.programNumber;
+ }
+
+ public int getServiceType() {
+ return mProto.serviceType;
+ }
+
+ public String getServiceTypeName() {
+ int serviceType = mProto.serviceType;
+ if (serviceType >= 0 && serviceType < ATSC_SERVICE_TYPE_NAMES.length) {
+ return ATSC_SERVICE_TYPE_NAMES[serviceType];
+ }
+ return ATSC_SERVICE_TYPE_NAME_RESERVED;
+ }
+
+ public int getVirtualMajor() {
+ return mProto.virtualMajor;
+ }
+
+ public int getVirtualMinor() {
+ return mProto.virtualMinor;
+ }
+
+ public int getFrequency() {
+ return mProto.frequency;
+ }
+
+ public String getModulation() {
+ return mProto.modulation;
+ }
+
+ public int getTsid() {
+ return mProto.tsid;
+ }
+
+ public int getVideoPid() {
+ return mProto.videoPid;
+ }
+
+ public synchronized void setVideoPid(int videoPid) {
+ mProto.videoPid = videoPid;
+ }
+
+ public int getVideoStreamType() {
+ return mProto.videoStreamType;
+ }
+
+ public int getAudioPid() {
+ if (mProto.audioTrackIndex == -1) {
+ return INVALID_PID;
+ }
+ return mProto.audioPids[mProto.audioTrackIndex];
+ }
+
+ public int getAudioStreamType() {
+ if (mProto.audioTrackIndex == -1) {
+ return INVALID_STREAMTYPE;
+ }
+ return mProto.audioStreamTypes[mProto.audioTrackIndex];
+ }
+
+ public List<Integer> getAudioPids() {
+ return Ints.asList(mProto.audioPids);
+ }
+
+ public synchronized void setAudioPids(List<Integer> audioPids) {
+ mProto.audioPids = Ints.toArray(audioPids);
+ }
+
+ public List<Integer> getAudioStreamTypes() {
+ return Ints.asList(mProto.audioStreamTypes);
+ }
+
+ public synchronized void setAudioStreamTypes(List<Integer> audioStreamTypes) {
+ mProto.audioStreamTypes = Ints.toArray(audioStreamTypes);
+ }
+
+ public int getPcrPid() {
+ return mProto.pcrPid;
+ }
+
+ public int getType() {
+ return mProto.type;
+ }
+
+ public synchronized void setFilepath(String filepath) {
+ mProto.filepath = filepath == null ? "" : filepath;
+ }
+
+ public String getFilepath() {
+ return mProto.filepath;
+ }
+
+ public synchronized void setVirtualMajor(int virtualMajor) {
+ mProto.virtualMajor = virtualMajor;
+ }
+
+ public synchronized void setVirtualMinor(int virtualMinor) {
+ mProto.virtualMinor = virtualMinor;
+ }
+
+ public synchronized void setShortName(String shortName) {
+ mProto.shortName = shortName == null ? "" : shortName;
+ }
+
+ public synchronized void setFrequency(int frequency) {
+ mProto.frequency = frequency;
+ }
+
+ public synchronized void setModulation(String modulation) {
+ mProto.modulation = modulation == null ? "" : modulation;
+ }
+
+ public boolean hasVideo() {
+ return mProto.videoPid != INVALID_PID;
+ }
+
+ public boolean hasAudio() {
+ return getAudioPid() != INVALID_PID;
+ }
+
+ public long getChannelId() {
+ return mProto.channelId;
+ }
+
+ public synchronized void setChannelId(long channelId) {
+ mProto.channelId = channelId;
+ }
+
+ public String getDisplayNumber() {
+ return getDisplayNumber(true);
+ }
+
+ public String getDisplayNumber(boolean ignoreZeroMinorNumber) {
+ if (mProto.virtualMajor != 0 && (mProto.virtualMinor != 0 || !ignoreZeroMinorNumber)) {
+ return String.format(
+ "%d%c%d", mProto.virtualMajor, CHANNEL_NUMBER_SEPARATOR, mProto.virtualMinor);
+ } else if (mProto.virtualMajor != 0) {
+ return Integer.toString(mProto.virtualMajor);
+ } else {
+ return Integer.toString(mProto.programNumber);
+ }
+ }
+
+ public String getDescription() {
+ return mProto.description;
+ }
+
+ @Override
+ public synchronized void setHasCaptionTrack() {
+ mProto.hasCaptionTrack = true;
+ }
+
+ @Override
+ public boolean hasCaptionTrack() {
+ return mProto.hasCaptionTrack;
+ }
+
+ @Override
+ public List<AtscAudioTrack> getAudioTracks() {
+ return Collections.unmodifiableList(Arrays.asList(mProto.audioTracks));
+ }
+
+ public synchronized void setAudioTracks(List<AtscAudioTrack> audioTracks) {
+ mProto.audioTracks = audioTracks.toArray(new AtscAudioTrack[audioTracks.size()]);
+ }
+
+ @Override
+ public List<AtscCaptionTrack> getCaptionTracks() {
+ return Collections.unmodifiableList(Arrays.asList(mProto.captionTracks));
+ }
+
+ public synchronized void setCaptionTracks(List<AtscCaptionTrack> captionTracks) {
+ mProto.captionTracks = captionTracks.toArray(new AtscCaptionTrack[captionTracks.size()]);
+ }
+
+ public synchronized void selectAudioTrack(int index) {
+ if (0 <= index && index < mProto.audioPids.length) {
+ mProto.audioTrackIndex = index;
+ } else {
+ mProto.audioTrackIndex = -1;
+ }
+ }
+
+ public synchronized void setRecordingProhibited(boolean recordingProhibited) {
+ mProto.recordingProhibited = recordingProhibited;
+ }
+
+ public boolean isRecordingProhibited() {
+ return mProto.recordingProhibited;
+ }
+
+ public synchronized void setVideoFormat(String videoFormat) {
+ mProto.videoFormat = videoFormat == null ? "" : videoFormat;
+ }
+
+ public String getVideoFormat() {
+ return mProto.videoFormat;
+ }
+
+ @Override
+ public String toString() {
+ switch (mProto.type) {
+ case Channel.TYPE_FILE:
+ return String.format(
+ "{%d-%d %s} Filepath: %s, ProgramNumber %d",
+ mProto.virtualMajor,
+ mProto.virtualMinor,
+ mProto.shortName,
+ mProto.filepath,
+ mProto.programNumber);
+ // case Channel.TYPE_TUNER:
+ default:
+ return String.format(
+ "{%d-%d %s} Frequency: %d, ProgramNumber %d",
+ mProto.virtualMajor,
+ mProto.virtualMinor,
+ mProto.shortName,
+ mProto.frequency,
+ mProto.programNumber);
+ }
+ }
+
+ @Override
+ public int compareTo(@NonNull TunerChannel channel) {
+ // In the same frequency, the program number acts as the sub-channel number.
+ int ret = getFrequency() - channel.getFrequency();
+ if (ret != 0) {
+ return ret;
+ }
+ ret = getProgramNumber() - channel.getProgramNumber();
+ if (ret != 0) {
+ return ret;
+ }
+ ret = StringUtils.compare(getName(), channel.getName());
+ if (ret != 0) {
+ return ret;
+ }
+ // For FileTsStreamer, file paths should be compared.
+ return StringUtils.compare(getFilepath(), channel.getFilepath());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof TunerChannel)) {
+ return false;
+ }
+ return compareTo((TunerChannel) o) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getFrequency(), getProgramNumber(), getName(), getFilepath());
+ }
+
+ // Serialization
+ public synchronized byte[] toByteArray() {
+ try {
+ return MessageNano.toByteArray(mProto);
+ } catch (Exception e) {
+ // Retry toByteArray. b/34197766
+ Log.w(
+ TAG,
+ "TunerChannel or its variables are modified in multiple thread without lock",
+ e);
+ return MessageNano.toByteArray(mProto);
+ }
+ }
+
+ public static TunerChannel parseFrom(byte[] data) {
+ if (data == null) {
+ return null;
+ }
+ try {
+ return new TunerChannel(TunerChannelProto.parseFrom(data));
+ } catch (IOException e) {
+ Log.e(TAG, "Could not parse from byte array", e);
+ return null;
+ }
+ }
+}