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