1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tv.samples.sampletunertvinput;
18 
19 import android.content.Context;
20 import android.media.tv.tuner.Tuner;
21 import android.media.tv.tuner.dvr.DvrPlayback;
22 import android.media.tv.tuner.dvr.DvrSettings;
23 import android.media.tv.tuner.filter.AvSettings;
24 import android.media.tv.tuner.filter.Filter;
25 import android.media.tv.tuner.filter.FilterCallback;
26 import android.media.tv.tuner.filter.FilterEvent;
27 import android.media.tv.tuner.filter.SectionSettingsWithSectionBits;
28 import android.media.tv.tuner.filter.TsFilterConfiguration;
29 import android.media.tv.tuner.frontend.DvbtFrontendSettings;
30 import android.os.Handler;
31 import android.os.HandlerExecutor;
32 import android.os.ParcelFileDescriptor;
33 import android.util.Log;
34 
35 import java.io.File;
36 import java.io.FileNotFoundException;
37 
38 public class SampleTunerTvInputUtils {
39     private static final String TAG = "SampleTunerTvInput";
40     private static final boolean DEBUG = true;
41 
42     private static final int AUDIO_TPID = 257;
43     private static final int VIDEO_TPID = 256;
44     private static final int SECTION_TPID = 255;
45     private static final int FILTER_BUFFER_SIZE = 16000000;
46 
47     private static final int STATUS_MASK = 0xf;
48     private static final int LOW_THRESHOLD = 0x1000;
49     private static final int HIGH_THRESHOLD = 0x07fff;
50     private static final int DVR_BUFFER_SIZE = 4000000;
51     private static final int PACKET_SIZE = 188;
52     private static final long FREQUENCY = 578000;
53     private static final int INPUT_FILE_MAX_SIZE = 1000000;
54 
configureDvrPlayback(Tuner tuner, Handler handler, int dataFormat)55     public static DvrPlayback configureDvrPlayback(Tuner tuner, Handler handler, int dataFormat) {
56         DvrPlayback dvr = tuner.openDvrPlayback(DVR_BUFFER_SIZE, new HandlerExecutor(handler),
57                 status -> {
58                     if (DEBUG) {
59                         Log.d(TAG, "onPlaybackStatusChanged status=" + status);
60                     }
61                 });
62         int res = dvr.configure(
63                 DvrSettings.builder()
64                         .setStatusMask(STATUS_MASK)
65                         .setLowThreshold(LOW_THRESHOLD)
66                         .setHighThreshold(HIGH_THRESHOLD)
67                         .setDataFormat(dataFormat)
68                         .setPacketSize(PACKET_SIZE)
69                         .build());
70         if (DEBUG) {
71             Log.d(TAG, "config res=" + res);
72         }
73         return dvr;
74     }
75 
readFilePlaybackInput(Context context, DvrPlayback dvr, String fileName)76     public static void readFilePlaybackInput(Context context, DvrPlayback dvr, String fileName) {
77         String testFile = context.getFilesDir().getAbsolutePath() + "/" + fileName;
78         File file = new File(testFile);
79         if (file.exists()) {
80             try {
81                 dvr.setFileDescriptor(
82                         ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE));
83             } catch (FileNotFoundException e) {
84                 Log.e(TAG, "Failed to create FD");
85             }
86         } else {
87             Log.w(TAG, "File not existing");
88         }
89 
90         long read = dvr.read(INPUT_FILE_MAX_SIZE);
91         if (DEBUG) {
92             Log.d(TAG, "read=" + read);
93         }
94     }
95 
tune(Tuner tuner, Handler handler)96     public static void tune(Tuner tuner, Handler handler) {
97         DvbtFrontendSettings feSettings = DvbtFrontendSettings.builder()
98                 .setFrequencyLong(FREQUENCY)
99                 .setTransmissionMode(DvbtFrontendSettings.TRANSMISSION_MODE_AUTO)
100                 .setBandwidth(DvbtFrontendSettings.BANDWIDTH_8MHZ)
101                 .setConstellation(DvbtFrontendSettings.CONSTELLATION_AUTO)
102                 .setHierarchy(DvbtFrontendSettings.HIERARCHY_AUTO)
103                 .setHighPriorityCodeRate(DvbtFrontendSettings.CODERATE_AUTO)
104                 .setLowPriorityCodeRate(DvbtFrontendSettings.CODERATE_AUTO)
105                 .setGuardInterval(DvbtFrontendSettings.GUARD_INTERVAL_AUTO)
106                 .setHighPriority(true)
107                 .setStandard(DvbtFrontendSettings.STANDARD_T)
108                 .build();
109 
110         tuner.setOnTuneEventListener(new HandlerExecutor(handler), tuneEvent -> {
111             if (DEBUG) {
112                 Log.d(TAG, "onTuneEvent " + tuneEvent);
113             }
114         });
115 
116         tuner.tune(feSettings);
117     }
118 
createSectionFilter(Tuner tuner, Handler handler, FilterCallback callback)119     public static Filter createSectionFilter(Tuner tuner, Handler handler,
120             FilterCallback callback) {
121         Filter sectionFilter = tuner.openFilter(Filter.TYPE_TS, Filter.SUBTYPE_SECTION,
122                 FILTER_BUFFER_SIZE, new HandlerExecutor(handler), callback);
123 
124         SectionSettingsWithSectionBits settings = SectionSettingsWithSectionBits
125                 .builder(Filter.TYPE_TS).build();
126 
127         sectionFilter.configure(
128                 TsFilterConfiguration.builder().setTpid(SECTION_TPID)
129                         .setSettings(settings).build());
130 
131         return sectionFilter;
132     }
133 
createAvFilter(Tuner tuner, Handler handler, FilterCallback callback, boolean isAudio)134     public static Filter createAvFilter(Tuner tuner, Handler handler,
135             FilterCallback callback, boolean isAudio) {
136         Filter avFilter = tuner.openFilter(Filter.TYPE_TS,
137                 isAudio ? Filter.SUBTYPE_AUDIO : Filter.SUBTYPE_VIDEO,
138                 FILTER_BUFFER_SIZE,
139                 new HandlerExecutor(handler),
140                 callback);
141 
142         AvSettings settings =
143                 AvSettings.builder(Filter.TYPE_TS, isAudio).setPassthrough(false).build();
144         avFilter.configure(
145                 TsFilterConfiguration.builder().
146                         setTpid(isAudio ? AUDIO_TPID : VIDEO_TPID)
147                         .setSettings(settings).build());
148         return avFilter;
149     }
150 
createDefaultLoggingFilterCallback(String filterType)151     public static FilterCallback createDefaultLoggingFilterCallback(String filterType) {
152         return new FilterCallback() {
153             @Override
154             public void onFilterEvent(Filter filter, FilterEvent[] events) {
155                 if (DEBUG) {
156                     Log.d(TAG, "onFilterEvent " + filterType + ", size=" + events.length);
157                 }
158                 for (int i = 0; i < events.length; i++) {
159                     if (DEBUG) {
160                         Log.d(TAG, "events[" + i + "] is "
161                                 + events[i].getClass().getSimpleName());
162                     }
163                 }
164             }
165 
166             @Override
167             public void onFilterStatusChanged(Filter filter, int status) {
168                 if (DEBUG) {
169                     Log.d(TAG, "onFilterStatusChanged " + filterType + ", status=" + status);
170                 }
171             }
172         };
173     }
174 }
175