1 /* 2 * Copyright 2019 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 android.media.tv.tuner.filter; 18 19 import android.annotation.BytesLong; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SystemApi; 24 import android.hardware.tv.tuner.V1_0.Constants; 25 import android.media.tv.tuner.Tuner; 26 import android.media.tv.tuner.Tuner.Result; 27 import android.media.tv.tuner.TunerUtils; 28 import android.media.tv.tuner.TunerVersionChecker; 29 30 import java.lang.annotation.Retention; 31 import java.lang.annotation.RetentionPolicy; 32 import java.util.concurrent.Executor; 33 34 /** 35 * Tuner data filter. 36 * 37 * <p>This class is used to filter wanted data according to the filter's configuration. 38 * 39 * @hide 40 */ 41 @SystemApi 42 public class Filter implements AutoCloseable { 43 /** @hide */ 44 @IntDef(prefix = "TYPE_", 45 value = {TYPE_TS, TYPE_MMTP, TYPE_IP, TYPE_TLV, TYPE_ALP}) 46 @Retention(RetentionPolicy.SOURCE) 47 public @interface Type {} 48 49 /** 50 * Undefined filter type. 51 */ 52 public static final int TYPE_UNDEFINED = 0; 53 /** 54 * TS filter type. 55 */ 56 public static final int TYPE_TS = Constants.DemuxFilterMainType.TS; 57 /** 58 * MMTP filter type. 59 */ 60 public static final int TYPE_MMTP = Constants.DemuxFilterMainType.MMTP; 61 /** 62 * IP filter type. 63 */ 64 public static final int TYPE_IP = Constants.DemuxFilterMainType.IP; 65 /** 66 * TLV filter type. 67 */ 68 public static final int TYPE_TLV = Constants.DemuxFilterMainType.TLV; 69 /** 70 * ALP filter type. 71 */ 72 public static final int TYPE_ALP = Constants.DemuxFilterMainType.ALP; 73 74 /** @hide */ 75 @IntDef(prefix = "SUBTYPE_", 76 value = {SUBTYPE_UNDEFINED, SUBTYPE_SECTION, SUBTYPE_PES, SUBTYPE_AUDIO, SUBTYPE_VIDEO, 77 SUBTYPE_DOWNLOAD, SUBTYPE_RECORD, SUBTYPE_TS, SUBTYPE_PCR, SUBTYPE_TEMI, 78 SUBTYPE_MMTP, SUBTYPE_NTP, SUBTYPE_IP_PAYLOAD, SUBTYPE_IP, 79 SUBTYPE_PAYLOAD_THROUGH, SUBTYPE_TLV, SUBTYPE_PTP, }) 80 @Retention(RetentionPolicy.SOURCE) 81 public @interface Subtype {} 82 /** 83 * Filter subtype undefined. 84 */ 85 public static final int SUBTYPE_UNDEFINED = 0; 86 /** 87 * Section filter subtype. 88 */ 89 public static final int SUBTYPE_SECTION = 1; 90 /** 91 * PES filter subtype. 92 */ 93 public static final int SUBTYPE_PES = 2; 94 /** 95 * Audio filter subtype. 96 */ 97 public static final int SUBTYPE_AUDIO = 3; 98 /** 99 * Video filter subtype. 100 */ 101 public static final int SUBTYPE_VIDEO = 4; 102 /** 103 * Download filter subtype. 104 */ 105 public static final int SUBTYPE_DOWNLOAD = 5; 106 /** 107 * Record filter subtype. 108 */ 109 public static final int SUBTYPE_RECORD = 6; 110 /** 111 * TS filter subtype. 112 */ 113 public static final int SUBTYPE_TS = 7; 114 /** 115 * PCR filter subtype. 116 */ 117 public static final int SUBTYPE_PCR = 8; 118 /** 119 * TEMI filter subtype. 120 */ 121 public static final int SUBTYPE_TEMI = 9; 122 /** 123 * MMTP filter subtype. 124 */ 125 public static final int SUBTYPE_MMTP = 10; 126 /** 127 * NTP filter subtype. 128 */ 129 public static final int SUBTYPE_NTP = 11; 130 /** 131 * Payload filter subtype. 132 */ 133 public static final int SUBTYPE_IP_PAYLOAD = 12; 134 /** 135 * IP filter subtype. 136 */ 137 public static final int SUBTYPE_IP = 13; 138 /** 139 * Payload through filter subtype. 140 */ 141 public static final int SUBTYPE_PAYLOAD_THROUGH = 14; 142 /** 143 * TLV filter subtype. 144 */ 145 public static final int SUBTYPE_TLV = 15; 146 /** 147 * PTP filter subtype. 148 */ 149 public static final int SUBTYPE_PTP = 16; 150 151 152 /** @hide */ 153 @IntDef(flag = true, prefix = "STATUS_", value = {STATUS_DATA_READY, STATUS_LOW_WATER, 154 STATUS_HIGH_WATER, STATUS_OVERFLOW}) 155 @Retention(RetentionPolicy.SOURCE) 156 public @interface Status {} 157 158 /** 159 * The status of a filter that the data in the filter buffer is ready to be read. 160 */ 161 public static final int STATUS_DATA_READY = Constants.DemuxFilterStatus.DATA_READY; 162 /** 163 * The status of a filter that the amount of available data in the filter buffer is at low 164 * level. 165 * 166 * The value is set to 25 percent of the buffer size by default. It can be changed when 167 * configuring the filter. 168 */ 169 public static final int STATUS_LOW_WATER = Constants.DemuxFilterStatus.LOW_WATER; 170 /** 171 * The status of a filter that the amount of available data in the filter buffer is at high 172 * level. 173 * The value is set to 75 percent of the buffer size by default. It can be changed when 174 * configuring the filter. 175 */ 176 public static final int STATUS_HIGH_WATER = Constants.DemuxFilterStatus.HIGH_WATER; 177 /** 178 * The status of a filter that the filter buffer is full and newly filtered data is being 179 * discarded. 180 */ 181 public static final int STATUS_OVERFLOW = Constants.DemuxFilterStatus.OVERFLOW; 182 183 /** @hide */ 184 @IntDef(flag = true, 185 prefix = "SCRAMBLING_STATUS_", 186 value = {SCRAMBLING_STATUS_UNKNOWN, SCRAMBLING_STATUS_NOT_SCRAMBLED, 187 SCRAMBLING_STATUS_SCRAMBLED}) 188 @Retention(RetentionPolicy.SOURCE) 189 public @interface ScramblingStatus {} 190 191 /** 192 * Content’s scrambling status is unknown 193 */ 194 public static final int SCRAMBLING_STATUS_UNKNOWN = 195 android.hardware.tv.tuner.V1_1.Constants.ScramblingStatus.UNKNOWN; 196 /** 197 * Content is not scrambled. 198 */ 199 public static final int SCRAMBLING_STATUS_NOT_SCRAMBLED = 200 android.hardware.tv.tuner.V1_1.Constants.ScramblingStatus.NOT_SCRAMBLED; 201 /** 202 * Content is scrambled. 203 */ 204 public static final int SCRAMBLING_STATUS_SCRAMBLED = 205 android.hardware.tv.tuner.V1_1.Constants.ScramblingStatus.SCRAMBLED; 206 207 /** @hide */ 208 @IntDef(flag = true, 209 prefix = "MONITOR_EVENT_", 210 value = {MONITOR_EVENT_SCRAMBLING_STATUS, MONITOR_EVENT_IP_CID_CHANGE}) 211 @Retention(RetentionPolicy.SOURCE) 212 public @interface MonitorEventMask {} 213 214 /** 215 * Monitor scrambling status change. 216 */ 217 public static final int MONITOR_EVENT_SCRAMBLING_STATUS = 218 android.hardware.tv.tuner.V1_1.Constants.DemuxFilterMonitorEventType.SCRAMBLING_STATUS; 219 /** 220 * Monitor ip cid change. 221 */ 222 public static final int MONITOR_EVENT_IP_CID_CHANGE = 223 android.hardware.tv.tuner.V1_1.Constants.DemuxFilterMonitorEventType.IP_CID_CHANGE; 224 225 private static final String TAG = "Filter"; 226 227 private long mNativeContext; 228 private FilterCallback mCallback; 229 private Executor mExecutor; 230 private final Object mCallbackLock = new Object(); 231 private final long mId; 232 private int mMainType; 233 private int mSubtype; 234 private Filter mSource; 235 private boolean mStarted; 236 private boolean mIsClosed = false; 237 private final Object mLock = new Object(); 238 nativeConfigureFilter( int type, int subType, FilterConfiguration settings)239 private native int nativeConfigureFilter( 240 int type, int subType, FilterConfiguration settings); nativeGetId()241 private native int nativeGetId(); nativeGetId64Bit()242 private native long nativeGetId64Bit(); nativeConfigureMonitorEvent(int monitorEventMask)243 private native int nativeConfigureMonitorEvent(int monitorEventMask); nativeSetDataSource(Filter source)244 private native int nativeSetDataSource(Filter source); nativeStartFilter()245 private native int nativeStartFilter(); nativeStopFilter()246 private native int nativeStopFilter(); nativeFlushFilter()247 private native int nativeFlushFilter(); nativeRead(byte[] buffer, long offset, long size)248 private native int nativeRead(byte[] buffer, long offset, long size); nativeClose()249 private native int nativeClose(); 250 251 // Called by JNI Filter(long id)252 private Filter(long id) { 253 mId = id; 254 } 255 onFilterStatus(int status)256 private void onFilterStatus(int status) { 257 synchronized (mCallbackLock) { 258 if (mCallback != null && mExecutor != null) { 259 mExecutor.execute(() -> mCallback.onFilterStatusChanged(this, status)); 260 } 261 } 262 } 263 onFilterEvent(FilterEvent[] events)264 private void onFilterEvent(FilterEvent[] events) { 265 synchronized (mCallbackLock) { 266 if (mCallback != null && mExecutor != null) { 267 mExecutor.execute(() -> mCallback.onFilterEvent(this, events)); 268 } 269 } 270 } 271 272 /** @hide */ setType(@ype int mainType, @Subtype int subtype)273 public void setType(@Type int mainType, @Subtype int subtype) { 274 mMainType = mainType; 275 mSubtype = TunerUtils.getFilterSubtype(mainType, subtype); 276 } 277 278 /** @hide */ setCallback(FilterCallback cb, Executor executor)279 public void setCallback(FilterCallback cb, Executor executor) { 280 synchronized (mCallbackLock) { 281 mCallback = cb; 282 mExecutor = executor; 283 } 284 } 285 286 /** @hide */ getCallback()287 public FilterCallback getCallback() { 288 synchronized (mCallbackLock) { 289 return mCallback; 290 } 291 } 292 293 /** 294 * Configures the filter. 295 * 296 * <p>Recofiguring must happen after stopping the filter. 297 * 298 * <p>When stopping, reconfiguring and restarting the filter, the client should discard all 299 * coming events until it receives {@link RestartEvent} through {@link FilterCallback} to avoid 300 * using the events from the previous configuration. 301 * 302 * @param config the configuration of the filter. 303 * @return result status of the operation. 304 */ 305 @Result configure(@onNull FilterConfiguration config)306 public int configure(@NonNull FilterConfiguration config) { 307 synchronized (mLock) { 308 TunerUtils.checkResourceState(TAG, mIsClosed); 309 Settings s = config.getSettings(); 310 int subType = (s == null) ? mSubtype : s.getType(); 311 if (mMainType != config.getType() || mSubtype != subType) { 312 throw new IllegalArgumentException("Invalid filter config. filter main type=" 313 + mMainType + ", filter subtype=" + mSubtype + ". config main type=" 314 + config.getType() + ", config subtype=" + subType); 315 } 316 return nativeConfigureFilter(config.getType(), subType, config); 317 } 318 } 319 320 /** 321 * Gets the filter Id in 32-bit. For any Tuner SoC that supports 64-bit filter architecture, 322 * use {@link #getIdLong()}. 323 */ getId()324 public int getId() { 325 synchronized (mLock) { 326 TunerUtils.checkResourceState(TAG, mIsClosed); 327 return nativeGetId(); 328 } 329 } 330 331 /** 332 * Gets the 64-bit filter Id. For any Tuner SoC that supports 32-bit filter architecture, 333 * use {@link #getId()}. 334 */ getIdLong()335 public long getIdLong() { 336 synchronized (mLock) { 337 TunerUtils.checkResourceState(TAG, mIsClosed); 338 return nativeGetId64Bit(); 339 } 340 } 341 342 /** 343 * Configure the Filter to monitor scrambling status and ip cid change. Set corresponding bit 344 * to monitor the change. Reset to stop monitoring. 345 * 346 * <p>{@link ScramblingStatusEvent} should be sent at the following two scenarios: 347 * <ul> 348 * <li>When this method is called with {@link #MONITOR_EVENT_SCRAMBLING_STATUS}, the first 349 * detected scrambling status should be sent. 350 * <li>When the Scrambling status transits into different status, event should be sent. 351 * <ul/> 352 * 353 * <p>{@link IpCidChangeEvent} should be sent at the following two scenarios: 354 * <ul> 355 * <li>When this method is called with {@link #MONITOR_EVENT_IP_CID_CHANGE}, the first 356 * detected CID for the IP should be sent. 357 * <li>When the CID is changed to different value for the IP filter, event should be sent. 358 * <ul/> 359 * 360 * <p>This configuration is only supported in Tuner 1.1 or higher version. Unsupported version 361 * will cause no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version 362 * information. 363 * 364 * @param monitorEventMask Types of event to be monitored. Set corresponding bit to 365 * monitor it. Reset to stop monitoring. 366 * @return result status of the operation. 367 */ 368 @Result setMonitorEventMask(@onitorEventMask int monitorEventMask)369 public int setMonitorEventMask(@MonitorEventMask int monitorEventMask) { 370 synchronized (mLock) { 371 TunerUtils.checkResourceState(TAG, mIsClosed); 372 if (!TunerVersionChecker.checkHigherOrEqualVersionTo( 373 TunerVersionChecker.TUNER_VERSION_1_1, "setMonitorEventMask")) { 374 return Tuner.RESULT_UNAVAILABLE; 375 } 376 return nativeConfigureMonitorEvent(monitorEventMask); 377 } 378 } 379 380 /** 381 * Sets the filter's data source. 382 * 383 * A filter uses demux as data source by default. If the data was packetized 384 * by multiple protocols, multiple filters may need to work together to 385 * extract all protocols' header. Then a filter's data source can be output 386 * from another filter. 387 * 388 * @param source the filter instance which provides data input. Switch to 389 * use demux as data source if the filter instance is NULL. 390 * @return result status of the operation. 391 * @throws IllegalStateException if the data source has been set. 392 */ 393 @Result setDataSource(@ullable Filter source)394 public int setDataSource(@Nullable Filter source) { 395 synchronized (mLock) { 396 TunerUtils.checkResourceState(TAG, mIsClosed); 397 if (mSource != null) { 398 throw new IllegalStateException("Data source is existing"); 399 } 400 int res = nativeSetDataSource(source); 401 if (res == Tuner.RESULT_SUCCESS) { 402 mSource = source; 403 } 404 return res; 405 } 406 } 407 408 /** 409 * Starts filtering data. 410 * 411 * <p>Does nothing if the filter is already started. 412 * 413 * <p>When stopping, reconfiguring and restarting the filter, the client should discard all 414 * coming events until it receives {@link RestartEvent} through {@link FilterCallback} to avoid 415 * using the events from the previous configuration. 416 * 417 * @return result status of the operation. 418 */ 419 @Result start()420 public int start() { 421 synchronized (mLock) { 422 TunerUtils.checkResourceState(TAG, mIsClosed); 423 return nativeStartFilter(); 424 } 425 } 426 427 428 /** 429 * Stops filtering data. 430 * 431 * <p>Does nothing if the filter is stopped or not started. 432 * 433 * <p>Filter must be stopped to reconfigure. 434 * 435 * <p>When stopping, reconfiguring and restarting the filter, the client should discard all 436 * coming events until it receives {@link RestartEvent} through {@link FilterCallback} to avoid 437 * using the events from the previous configuration. 438 * 439 * @return result status of the operation. 440 */ 441 @Result stop()442 public int stop() { 443 synchronized (mLock) { 444 TunerUtils.checkResourceState(TAG, mIsClosed); 445 return nativeStopFilter(); 446 } 447 } 448 449 /** 450 * Flushes the filter. 451 * 452 * <p>The data which is already produced by filter but not consumed yet will 453 * be cleared. 454 * 455 * @return result status of the operation. 456 */ 457 @Result flush()458 public int flush() { 459 synchronized (mLock) { 460 TunerUtils.checkResourceState(TAG, mIsClosed); 461 return nativeFlushFilter(); 462 } 463 } 464 465 /** 466 * Copies filtered data from filter output to the given byte array. 467 * 468 * @param buffer the buffer to store the filtered data. 469 * @param offset the index of the first byte in {@code buffer} to write. 470 * @param size the maximum number of bytes to read. 471 * @return the number of bytes read. 472 */ read(@onNull byte[] buffer, @BytesLong long offset, @BytesLong long size)473 public int read(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) { 474 synchronized (mLock) { 475 TunerUtils.checkResourceState(TAG, mIsClosed); 476 size = Math.min(size, buffer.length - offset); 477 return nativeRead(buffer, offset, size); 478 } 479 } 480 481 /** 482 * Stops filtering data and releases the Filter instance. 483 */ 484 @Override close()485 public void close() { 486 synchronized (mLock) { 487 if (mIsClosed) { 488 return; 489 } 490 int res = nativeClose(); 491 if (res != Tuner.RESULT_SUCCESS) { 492 TunerUtils.throwExceptionForResult(res, "Failed to close filter."); 493 } else { 494 mIsClosed = true; 495 } 496 } 497 } 498 } 499