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.NonNull; 21 import android.annotation.SystemApi; 22 import android.app.ActivityManager; 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.util.Log; 29 30 import com.android.internal.util.FrameworkStatsLog; 31 32 import java.util.concurrent.Executor; 33 34 35 /** 36 * Digital Video Record (DVR) recorder class which provides record control on Demux's output buffer. 37 * 38 * @hide 39 */ 40 @SystemApi 41 public class DvrRecorder implements AutoCloseable { 42 private static final String TAG = "TvTunerRecord"; 43 private long mNativeContext; 44 private OnRecordStatusChangedListener mListener; 45 private Executor mExecutor; 46 private int mUserId; 47 private static int sInstantId = 0; 48 private int mSegmentId = 0; 49 private int mOverflow; 50 nativeAttachFilter(Filter filter)51 private native int nativeAttachFilter(Filter filter); nativeDetachFilter(Filter filter)52 private native int nativeDetachFilter(Filter filter); nativeConfigureDvr(DvrSettings settings)53 private native int nativeConfigureDvr(DvrSettings settings); nativeStartDvr()54 private native int nativeStartDvr(); nativeStopDvr()55 private native int nativeStopDvr(); nativeFlushDvr()56 private native int nativeFlushDvr(); nativeClose()57 private native int nativeClose(); nativeSetFileDescriptor(int fd)58 private native void nativeSetFileDescriptor(int fd); nativeWrite(long size)59 private native long nativeWrite(long size); nativeWrite(byte[] bytes, long offset, long size)60 private native long nativeWrite(byte[] bytes, long offset, long size); 61 DvrRecorder()62 private DvrRecorder() { 63 mUserId = ActivityManager.getCurrentUser(); 64 mSegmentId = (sInstantId & 0x0000ffff) << 16; 65 sInstantId++; 66 } 67 68 /** @hide */ setListener( @onNull Executor executor, @NonNull OnRecordStatusChangedListener listener)69 public void setListener( 70 @NonNull Executor executor, @NonNull OnRecordStatusChangedListener listener) { 71 mExecutor = executor; 72 mListener = listener; 73 } 74 onRecordStatusChanged(int status)75 private void onRecordStatusChanged(int status) { 76 if (status == Filter.STATUS_OVERFLOW) { 77 mOverflow++; 78 } 79 if (mExecutor != null && mListener != null) { 80 mExecutor.execute(() -> mListener.onRecordStatusChanged(status)); 81 } 82 } 83 84 85 /** 86 * Attaches a filter to DVR interface for recording. 87 * 88 * <p>There can be multiple filters attached. Attached filters are independent, so the order 89 * doesn't matter. 90 * 91 * @param filter the filter to be attached. 92 * @return result status of the operation. 93 */ 94 @Result attachFilter(@onNull Filter filter)95 public int attachFilter(@NonNull Filter filter) { 96 return nativeAttachFilter(filter); 97 } 98 99 /** 100 * Detaches a filter from DVR interface. 101 * 102 * @param filter the filter to be detached. 103 * @return result status of the operation. 104 */ 105 @Result detachFilter(@onNull Filter filter)106 public int detachFilter(@NonNull Filter filter) { 107 return nativeDetachFilter(filter); 108 } 109 110 /** 111 * Configures the DVR. 112 * 113 * @param settings the settings of the DVR interface. 114 * @return result status of the operation. 115 */ 116 @Result configure(@onNull DvrSettings settings)117 public int configure(@NonNull DvrSettings settings) { 118 return nativeConfigureDvr(settings); 119 } 120 121 /** 122 * Starts DVR. 123 * 124 * <p>Starts consuming playback data or producing data for recording. 125 * <p>Does nothing if the filter is stopped or not started.</p> 126 * 127 * @return result status of the operation. 128 */ 129 @Result start()130 public int start() { 131 mSegmentId = (mSegmentId & 0xffff0000) | (((mSegmentId & 0x0000ffff) + 1) & 0x0000ffff); 132 mOverflow = 0; 133 Log.d(TAG, "Write Stats Log for Record."); 134 FrameworkStatsLog 135 .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId, 136 FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__RECORD, 137 FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STARTED, mSegmentId, 0); 138 return nativeStartDvr(); 139 } 140 141 /** 142 * Stops DVR. 143 * 144 * <p>Stops consuming playback data or producing data for recording. 145 * 146 * @return result status of the operation. 147 */ 148 @Result stop()149 public int stop() { 150 Log.d(TAG, "Write Stats Log for Playback."); 151 FrameworkStatsLog 152 .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId, 153 FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__RECORD, 154 FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STOPPED, mSegmentId, mOverflow); 155 return nativeStopDvr(); 156 } 157 158 /** 159 * Flushed DVR data. 160 * 161 * <p>The data in DVR buffer is cleared. 162 * 163 * @return result status of the operation. 164 */ 165 @Result flush()166 public int flush() { 167 return nativeFlushDvr(); 168 } 169 170 /** 171 * Closes the DVR instance to release resources. 172 */ 173 @Override close()174 public void close() { 175 int res = nativeClose(); 176 if (res != Tuner.RESULT_SUCCESS) { 177 TunerUtils.throwExceptionForResult(res, "failed to close DVR recorder"); 178 } 179 } 180 181 /** 182 * Sets file descriptor to write data. 183 * 184 * <p>When a write operation of the filter object is happening, this method should not be 185 * called. 186 * 187 * @param fd the file descriptor to write data. 188 * @see #write(long) 189 * @see #write(byte[], long, long) 190 */ setFileDescriptor(@onNull ParcelFileDescriptor fd)191 public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) { 192 nativeSetFileDescriptor(fd.getFd()); 193 } 194 195 /** 196 * Writes recording data to file. 197 * 198 * @param size the maximum number of bytes to write. 199 * @return the number of bytes written. 200 */ 201 @BytesLong write(@ytesLong long size)202 public long write(@BytesLong long size) { 203 return nativeWrite(size); 204 } 205 206 /** 207 * Writes recording data to buffer. 208 * 209 * @param bytes the byte array stores the data to be written to DVR. 210 * @param offset the index of the first byte in {@code bytes} to be written to DVR. 211 * @param size the maximum number of bytes to write. 212 * @return the number of bytes written. 213 */ 214 @BytesLong write(@onNull byte[] bytes, @BytesLong long offset, @BytesLong long size)215 public long write(@NonNull byte[] bytes, @BytesLong long offset, @BytesLong long size) { 216 if (size + offset > bytes.length) { 217 throw new ArrayIndexOutOfBoundsException( 218 "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size); 219 } 220 return nativeWrite(bytes, offset, size); 221 } 222 } 223