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.app.ActivityManager; 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.filter.Filter; 29 import android.os.ParcelFileDescriptor; 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 = Constants.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 Constants.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 Constants.PlaybackStatus.SPACE_ALMOST_FULL; 74 /** 75 * The space of the playback is full. 76 */ 77 public static final int PLAYBACK_STATUS_FULL = Constants.PlaybackStatus.SPACE_FULL; 78 79 private static final String TAG = "TvTunerPlayback"; 80 81 private long mNativeContext; 82 private OnPlaybackStatusChangedListener mListener; 83 private Executor mExecutor; 84 private int mUserId; 85 private static int sInstantId = 0; 86 private int mSegmentId = 0; 87 private int mUnderflow; 88 nativeAttachFilter(Filter filter)89 private native int nativeAttachFilter(Filter filter); nativeDetachFilter(Filter filter)90 private native int nativeDetachFilter(Filter filter); nativeConfigureDvr(DvrSettings settings)91 private native int nativeConfigureDvr(DvrSettings settings); nativeStartDvr()92 private native int nativeStartDvr(); nativeStopDvr()93 private native int nativeStopDvr(); nativeFlushDvr()94 private native int nativeFlushDvr(); nativeClose()95 private native int nativeClose(); nativeSetFileDescriptor(int fd)96 private native void nativeSetFileDescriptor(int fd); nativeRead(long size)97 private native long nativeRead(long size); nativeRead(byte[] bytes, long offset, long size)98 private native long nativeRead(byte[] bytes, long offset, long size); 99 DvrPlayback()100 private DvrPlayback() { 101 mUserId = ActivityManager.getCurrentUser(); 102 mSegmentId = (sInstantId & 0x0000ffff) << 16; 103 sInstantId++; 104 } 105 106 /** @hide */ setListener( @onNull Executor executor, @NonNull OnPlaybackStatusChangedListener listener)107 public void setListener( 108 @NonNull Executor executor, @NonNull OnPlaybackStatusChangedListener listener) { 109 mExecutor = executor; 110 mListener = listener; 111 } 112 onPlaybackStatusChanged(int status)113 private void onPlaybackStatusChanged(int status) { 114 if (status == PLAYBACK_STATUS_EMPTY) { 115 mUnderflow++; 116 } 117 if (mExecutor != null && mListener != null) { 118 mExecutor.execute(() -> mListener.onPlaybackStatusChanged(status)); 119 } 120 } 121 122 123 /** 124 * Attaches a filter to DVR interface for playback. 125 * 126 * <p>This method will be deprecated. Now it's a no-op. 127 * <p>Filters opened by {@link Tuner#openFilter} are used for DVR playback. 128 * 129 * @param filter the filter to be attached. 130 * @return result status of the operation. 131 */ 132 @Result attachFilter(@onNull Filter filter)133 public int attachFilter(@NonNull Filter filter) { 134 // no-op 135 return Tuner.RESULT_UNAVAILABLE; 136 } 137 138 /** 139 * Detaches a filter from DVR interface. 140 * 141 * <p>This method will be deprecated. Now it's a no-op. 142 * <p>Filters opened by {@link Tuner#openFilter} are used for DVR playback. 143 * 144 * @param filter the filter to be detached. 145 * @return result status of the operation. 146 */ 147 @Result detachFilter(@onNull Filter filter)148 public int detachFilter(@NonNull Filter filter) { 149 // no-op 150 return Tuner.RESULT_UNAVAILABLE; 151 } 152 153 /** 154 * Configures the DVR. 155 * 156 * @param settings the settings of the DVR interface. 157 * @return result status of the operation. 158 */ 159 @Result configure(@onNull DvrSettings settings)160 public int configure(@NonNull DvrSettings settings) { 161 return nativeConfigureDvr(settings); 162 } 163 164 /** 165 * Starts DVR. 166 * 167 * <p>Starts consuming playback data or producing data for recording. 168 * 169 * @return result status of the operation. 170 */ 171 @Result start()172 public int start() { 173 mSegmentId = (mSegmentId & 0xffff0000) | (((mSegmentId & 0x0000ffff) + 1) & 0x0000ffff); 174 mUnderflow = 0; 175 Log.d(TAG, "Write Stats Log for Playback."); 176 FrameworkStatsLog 177 .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId, 178 FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__PLAYBACK, 179 FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STARTED, mSegmentId, 0); 180 return nativeStartDvr(); 181 } 182 183 /** 184 * Stops DVR. 185 * 186 * <p>Stops consuming playback data or producing data for recording. 187 * <p>Does nothing if the filter is stopped or not started.</p> 188 * 189 * @return result status of the operation. 190 */ 191 @Result stop()192 public int stop() { 193 Log.d(TAG, "Write Stats Log for Playback."); 194 FrameworkStatsLog 195 .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId, 196 FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__PLAYBACK, 197 FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STOPPED, mSegmentId, mUnderflow); 198 return nativeStopDvr(); 199 } 200 201 /** 202 * Flushed DVR data. 203 * 204 * <p>The data in DVR buffer is cleared. 205 * 206 * @return result status of the operation. 207 */ 208 @Result flush()209 public int flush() { 210 return nativeFlushDvr(); 211 } 212 213 /** 214 * Closes the DVR instance to release resources. 215 */ 216 @Override close()217 public void close() { 218 int res = nativeClose(); 219 if (res != Tuner.RESULT_SUCCESS) { 220 TunerUtils.throwExceptionForResult(res, "failed to close DVR playback"); 221 } 222 } 223 224 /** 225 * Sets file descriptor to read data. 226 * 227 * <p>When a read operation of the filter object is happening, this method should not be 228 * called. 229 * 230 * @param fd the file descriptor to read data. 231 * @see #read(long) 232 * @see #read(byte[], long, long) 233 */ setFileDescriptor(@onNull ParcelFileDescriptor fd)234 public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) { 235 nativeSetFileDescriptor(fd.getFd()); 236 } 237 238 /** 239 * Reads data from the file for DVR playback. 240 * 241 * @param size the maximum number of bytes to read. 242 * @return the number of bytes read. 243 */ 244 @BytesLong read(@ytesLong long size)245 public long read(@BytesLong long size) { 246 return nativeRead(size); 247 } 248 249 /** 250 * Reads data from the buffer for DVR playback and copies to the given byte array. 251 * 252 * @param bytes the byte array to store the data. 253 * @param offset the index of the first byte in {@code bytes} to copy to. 254 * @param size the maximum number of bytes to read. 255 * @return the number of bytes read. 256 */ 257 @BytesLong read(@onNull byte[] bytes, @BytesLong long offset, @BytesLong long size)258 public long read(@NonNull byte[] bytes, @BytesLong long offset, @BytesLong long size) { 259 if (size + offset > bytes.length) { 260 throw new ArrayIndexOutOfBoundsException( 261 "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size); 262 } 263 return nativeRead(bytes, offset, size); 264 } 265 } 266