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.dvr; 18 19 import android.annotation.BytesLong; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.SystemApi; 23 import android.media.tv.tuner.Tuner; 24 import android.media.tv.tuner.Tuner.Result; 25 import android.media.tv.tuner.TunerUtils; 26 import android.media.tv.tuner.TunerVersionChecker; 27 import android.media.tv.tuner.filter.Filter; 28 import android.os.ParcelFileDescriptor; 29 import android.os.Process; 30 import android.util.Log; 31 32 import com.android.internal.util.FrameworkStatsLog; 33 34 import java.lang.annotation.Retention; 35 import java.lang.annotation.RetentionPolicy; 36 import java.util.concurrent.Executor; 37 38 /** 39 * Digital Video Record (DVR) class which provides playback control on Demux's input buffer. 40 * 41 * <p>It's used to play recorded programs. 42 * 43 * @hide 44 */ 45 @SystemApi 46 public class DvrPlayback implements AutoCloseable { 47 48 49 /** @hide */ 50 @Retention(RetentionPolicy.SOURCE) 51 @IntDef(prefix = "PLAYBACK_STATUS_", 52 value = {PLAYBACK_STATUS_EMPTY, PLAYBACK_STATUS_ALMOST_EMPTY, 53 PLAYBACK_STATUS_ALMOST_FULL, PLAYBACK_STATUS_FULL}) 54 @interface PlaybackStatus {} 55 56 /** 57 * The space of the playback is empty. 58 */ 59 public static final int PLAYBACK_STATUS_EMPTY = 60 android.hardware.tv.tuner.PlaybackStatus.SPACE_EMPTY; 61 /** 62 * The space of the playback is almost empty. 63 * 64 * <p> the threshold is set in {@link DvrSettings}. 65 */ 66 public static final int PLAYBACK_STATUS_ALMOST_EMPTY = 67 android.hardware.tv.tuner.PlaybackStatus.SPACE_ALMOST_EMPTY; 68 /** 69 * The space of the playback is almost full. 70 * 71 * <p> the threshold is set in {@link DvrSettings}. 72 */ 73 public static final int PLAYBACK_STATUS_ALMOST_FULL = 74 android.hardware.tv.tuner.PlaybackStatus.SPACE_ALMOST_FULL; 75 /** 76 * The space of the playback is full. 77 */ 78 public static final int PLAYBACK_STATUS_FULL = 79 android.hardware.tv.tuner.PlaybackStatus.SPACE_FULL; 80 81 private static final String TAG = "TvTunerPlayback"; 82 83 private long mNativeContext; 84 private OnPlaybackStatusChangedListener mListener; 85 private Executor mExecutor; 86 private int mUserId; 87 private static int sInstantId = 0; 88 private int mSegmentId = 0; 89 private int mUnderflow; 90 private final Object mListenerLock = new Object(); 91 nativeAttachFilter(Filter filter)92 private native int nativeAttachFilter(Filter filter); nativeDetachFilter(Filter filter)93 private native int nativeDetachFilter(Filter filter); nativeConfigureDvr(DvrSettings settings)94 private native int nativeConfigureDvr(DvrSettings settings); nativeSetStatusCheckIntervalHint(long durationInMs)95 private native int nativeSetStatusCheckIntervalHint(long durationInMs); nativeStartDvr()96 private native int nativeStartDvr(); nativeStopDvr()97 private native int nativeStopDvr(); nativeFlushDvr()98 private native int nativeFlushDvr(); nativeClose()99 private native int nativeClose(); nativeSetFileDescriptor(int fd)100 private native void nativeSetFileDescriptor(int fd); nativeRead(long size)101 private native long nativeRead(long size); nativeRead(byte[] bytes, long offset, long size)102 private native long nativeRead(byte[] bytes, long offset, long size); nativeSeek(long pos)103 private native long nativeSeek(long pos); 104 DvrPlayback()105 private DvrPlayback() { 106 mUserId = Process.myUid(); 107 mSegmentId = (sInstantId & 0x0000ffff) << 16; 108 sInstantId++; 109 } 110 111 /** @hide */ setListener( @onNull Executor executor, @NonNull OnPlaybackStatusChangedListener listener)112 public void setListener( 113 @NonNull Executor executor, @NonNull OnPlaybackStatusChangedListener listener) { 114 synchronized (mListenerLock) { 115 mExecutor = executor; 116 mListener = listener; 117 } 118 } 119 onPlaybackStatusChanged(int status)120 private void onPlaybackStatusChanged(int status) { 121 if (status == PLAYBACK_STATUS_EMPTY) { 122 mUnderflow++; 123 } 124 synchronized (mListenerLock) { 125 if (mExecutor != null && mListener != null) { 126 mExecutor.execute(() -> { 127 synchronized (mListenerLock) { 128 if (mListener != null) { 129 mListener.onPlaybackStatusChanged(status); 130 } 131 } 132 }); 133 } 134 } 135 } 136 137 138 /** 139 * Attaches a filter to DVR interface for playback. 140 * 141 * @deprecated attaching filters is not valid in Dvr Playback use case. This API is a no-op. 142 * Filters opened by {@link Tuner#openFilter} are used for DVR playback. 143 * 144 * @param filter the filter to be attached. 145 * @return result status of the operation. 146 */ 147 @Result 148 @Deprecated attachFilter(@onNull Filter filter)149 public int attachFilter(@NonNull Filter filter) { 150 // no-op 151 return Tuner.RESULT_UNAVAILABLE; 152 } 153 154 /** 155 * Detaches a filter from DVR interface. 156 * 157 * @deprecated detaching filters is not valid in Dvr Playback use case. This API is a no-op. 158 * Filters opened by {@link Tuner#openFilter} are used for DVR playback. 159 * 160 * @param filter the filter to be detached. 161 * @return result status of the operation. 162 */ 163 @Result 164 @Deprecated detachFilter(@onNull Filter filter)165 public int detachFilter(@NonNull Filter filter) { 166 // no-op 167 return Tuner.RESULT_UNAVAILABLE; 168 } 169 170 /** 171 * Configures the DVR. 172 * 173 * @param settings the settings of the DVR interface. 174 * @return result status of the operation. 175 */ 176 @Result configure(@onNull DvrSettings settings)177 public int configure(@NonNull DvrSettings settings) { 178 return nativeConfigureDvr(settings); 179 } 180 181 /** 182 * Set playback buffer status check time interval. 183 * 184 * This status check time interval will be used by the Dvr to decide how often to evaluate 185 * data. The default value will be decided by HAL if it’s not set. 186 * 187 * <p>This functionality is only available in Tuner version 3.0 and higher and will otherwise 188 * return a {@link Tuner#RESULT_UNAVAILABLE}. Use {@link TunerVersionChecker#getTunerVersion()} 189 * to get the version information. 190 * 191 * @param durationInMs specifies the duration of the delay in milliseconds. 192 * 193 * @return one of the following results: 194 * {@link Tuner#RESULT_SUCCESS} if succeed, 195 * {@link Tuner#RESULT_UNAVAILABLE} if Dvr is unavailable or unsupported HAL versions, 196 * {@link Tuner#RESULT_NOT_INITIALIZED} if Dvr is not initialized, 197 * {@link Tuner#RESULT_INVALID_STATE} if Dvr is in a wrong state, 198 * {@link Tuner#RESULT_INVALID_ARGUMENT} if the input parameter is invalid. 199 */ 200 @Result setPlaybackBufferStatusCheckIntervalHint(long durationInMs)201 public int setPlaybackBufferStatusCheckIntervalHint(long durationInMs) { 202 if (!TunerVersionChecker.checkHigherOrEqualVersionTo( 203 TunerVersionChecker.TUNER_VERSION_3_0, "Set status check interval hint")) { 204 // no-op 205 return Tuner.RESULT_UNAVAILABLE; 206 } 207 return nativeSetStatusCheckIntervalHint(durationInMs); 208 } 209 210 /** 211 * Starts DVR. 212 * 213 * <p>Starts consuming playback data or producing data for recording. 214 * 215 * @return result status of the operation. 216 */ 217 @Result start()218 public int start() { 219 mSegmentId = (mSegmentId & 0xffff0000) | (((mSegmentId & 0x0000ffff) + 1) & 0x0000ffff); 220 mUnderflow = 0; 221 Log.d(TAG, "Write Stats Log for Playback."); 222 FrameworkStatsLog 223 .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId, 224 FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__PLAYBACK, 225 FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STARTED, mSegmentId, 0); 226 return nativeStartDvr(); 227 } 228 229 /** 230 * Stops DVR. 231 * 232 * <p>Stops consuming playback data or producing data for recording. 233 * <p>Does nothing if the filter is stopped or not started.</p> 234 * 235 * @return result status of the operation. 236 */ 237 @Result stop()238 public int stop() { 239 Log.d(TAG, "Write Stats Log for Playback."); 240 FrameworkStatsLog 241 .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId, 242 FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__PLAYBACK, 243 FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STOPPED, mSegmentId, mUnderflow); 244 return nativeStopDvr(); 245 } 246 247 /** 248 * Flushed DVR data. 249 * 250 * <p>The data in DVR buffer is cleared. 251 * 252 * @return result status of the operation. 253 */ 254 @Result flush()255 public int flush() { 256 return nativeFlushDvr(); 257 } 258 259 /** 260 * Closes the DVR instance to release resources. 261 */ 262 @Override close()263 public void close() { 264 int res = nativeClose(); 265 if (res != Tuner.RESULT_SUCCESS) { 266 TunerUtils.throwExceptionForResult(res, "failed to close DVR playback"); 267 } 268 } 269 270 /** 271 * Sets file descriptor to read data. 272 * 273 * <p>When a read operation of the filter object is happening, this method should not be 274 * called. 275 * 276 * @param fd the file descriptor to read data. 277 * @see #read(long) 278 * @see #seek(long) 279 */ setFileDescriptor(@onNull ParcelFileDescriptor fd)280 public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) { 281 nativeSetFileDescriptor(fd.getFd()); 282 } 283 284 /** 285 * Reads data from the file for DVR playback. 286 * 287 * @param size the maximum number of bytes to read. 288 * @return the number of bytes read. 289 */ 290 @BytesLong read(@ytesLong long size)291 public long read(@BytesLong long size) { 292 return nativeRead(size); 293 } 294 295 /** 296 * Reads data from the buffer for DVR playback. 297 * 298 * @param buffer the byte array where DVR reads data from. 299 * @param offset the index of the first byte in {@code buffer} to read. 300 * @param size the maximum number of bytes to read. 301 * @return the number of bytes read. 302 */ 303 @BytesLong read(@onNull byte[] buffer, @BytesLong long offset, @BytesLong long size)304 public long read(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) { 305 if (size + offset > buffer.length) { 306 throw new ArrayIndexOutOfBoundsException( 307 "Array length=" + buffer.length + ", offset=" + offset + ", size=" + size); 308 } 309 return nativeRead(buffer, offset, size); 310 } 311 312 /** 313 * Sets the file pointer offset of the file descriptor. 314 * 315 * @param position the offset position, measured in bytes from the beginning of the file. 316 * @return the new offset position. On error, {@code -1} is returned. 317 */ 318 @BytesLong seek(@ytesLong long position)319 public long seek(@BytesLong long position) { 320 return nativeSeek(position); 321 } 322 } 323