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