• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.media.tv.tuner.Tuner;
23 import android.media.tv.tuner.Tuner.Result;
24 import android.media.tv.tuner.TunerUtils;
25 import android.media.tv.tuner.filter.Filter;
26 import android.os.ParcelFileDescriptor;
27 import android.os.Process;
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     private Boolean mIsStopped = true;
51     private final Object mListenerLock = new Object();
52 
nativeAttachFilter(Filter filter)53     private native int nativeAttachFilter(Filter filter);
nativeDetachFilter(Filter filter)54     private native int nativeDetachFilter(Filter filter);
nativeConfigureDvr(DvrSettings settings)55     private native int nativeConfigureDvr(DvrSettings settings);
nativeStartDvr()56     private native int nativeStartDvr();
nativeStopDvr()57     private native int nativeStopDvr();
nativeFlushDvr()58     private native int nativeFlushDvr();
nativeClose()59     private native int nativeClose();
nativeSetFileDescriptor(int fd)60     private native void nativeSetFileDescriptor(int fd);
nativeWrite(long size)61     private native long nativeWrite(long size);
nativeWrite(byte[] bytes, long offset, long size)62     private native long nativeWrite(byte[] bytes, long offset, long size);
63 
DvrRecorder()64     private DvrRecorder() {
65         mUserId = Process.myUid();
66         mSegmentId = (sInstantId & 0x0000ffff) << 16;
67         sInstantId++;
68     }
69 
70     /** @hide */
setListener( @onNull Executor executor, @NonNull OnRecordStatusChangedListener listener)71     public void setListener(
72             @NonNull Executor executor, @NonNull OnRecordStatusChangedListener listener) {
73         synchronized (mListenerLock) {
74             mExecutor = executor;
75             mListener = listener;
76         }
77     }
78 
onRecordStatusChanged(int status)79     private void onRecordStatusChanged(int status) {
80         if (status == Filter.STATUS_OVERFLOW) {
81             mOverflow++;
82         }
83         synchronized (mListenerLock) {
84             if (mExecutor != null && mListener != null) {
85                 mExecutor.execute(() -> mListener.onRecordStatusChanged(status));
86             }
87         }
88     }
89 
90 
91     /**
92      * Attaches a filter to DVR interface for recording.
93      *
94      * <p>There can be multiple filters attached. Attached filters are independent, so the order
95      * doesn't matter.
96      *
97      * @param filter the filter to be attached.
98      * @return result status of the operation.
99      */
100     @Result
attachFilter(@onNull Filter filter)101     public int attachFilter(@NonNull Filter filter) {
102         return nativeAttachFilter(filter);
103     }
104 
105     /**
106      * Detaches a filter from DVR interface.
107      *
108      * @param filter the filter to be detached.
109      * @return result status of the operation.
110      */
111     @Result
detachFilter(@onNull Filter filter)112     public int detachFilter(@NonNull Filter filter) {
113         return nativeDetachFilter(filter);
114     }
115 
116     /**
117      * Configures the DVR.
118      *
119      * @param settings the settings of the DVR interface.
120      * @return result status of the operation.
121      */
122     @Result
configure(@onNull DvrSettings settings)123     public int configure(@NonNull DvrSettings settings) {
124         return nativeConfigureDvr(settings);
125     }
126 
127     /**
128      * Starts DVR.
129      *
130      * <p>Starts consuming playback data or producing data for recording.
131      * <p>Does nothing if the filter is stopped or not started.</p>
132      *
133      * @return result status of the operation.
134      */
135     @Result
start()136     public int start() {
137         mSegmentId =  (mSegmentId & 0xffff0000) | (((mSegmentId & 0x0000ffff) + 1) & 0x0000ffff);
138         mOverflow = 0;
139         Log.d(TAG, "Write Stats Log for Record.");
140         FrameworkStatsLog
141                 .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
142                     FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__RECORD,
143                     FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STARTED, mSegmentId, 0);
144         synchronized (mIsStopped) {
145             int result = nativeStartDvr();
146             if (result == Tuner.RESULT_SUCCESS) {
147                 mIsStopped = false;
148             }
149             return result;
150         }
151     }
152 
153     /**
154      * Stops DVR.
155      *
156      * <p>Stops consuming playback data or producing data for recording.
157      *
158      * @return result status of the operation.
159      */
160     @Result
stop()161     public int stop() {
162         Log.d(TAG, "Write Stats Log for Playback.");
163         FrameworkStatsLog
164                 .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
165                     FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__RECORD,
166                     FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STOPPED, mSegmentId, mOverflow);
167         synchronized (mIsStopped) {
168             int result = nativeStopDvr();
169             if (result == Tuner.RESULT_SUCCESS) {
170                 mIsStopped = true;
171             }
172             return result;
173         }
174     }
175 
176     /**
177      * Flushed DVR data.
178      *
179      * <p>The data in DVR buffer is cleared.
180      *
181      * @return result status of the operation.
182      */
183     @Result
flush()184     public int flush() {
185         synchronized (mIsStopped) {
186             if (mIsStopped) {
187                 return nativeFlushDvr();
188             }
189             Log.w(TAG, "Cannot flush non-stopped Record DVR.");
190             return Tuner.RESULT_INVALID_STATE;
191         }
192     }
193 
194     /**
195      * Closes the DVR instance to release resources.
196      */
197     @Override
close()198     public void close() {
199         int res = nativeClose();
200         if (res != Tuner.RESULT_SUCCESS) {
201             TunerUtils.throwExceptionForResult(res, "failed to close DVR recorder");
202         }
203     }
204 
205     /**
206      * Sets file descriptor to write data.
207      *
208      * <p>When a write operation of the filter object is happening, this method should not be
209      * called.
210      *
211      * @param fd the file descriptor to write data.
212      * @see #write(long)
213      * @see #write(byte[], long, long)
214      */
setFileDescriptor(@onNull ParcelFileDescriptor fd)215     public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) {
216         nativeSetFileDescriptor(fd.getFd());
217     }
218 
219     /**
220      * Writes recording data to file.
221      *
222      * @param size the maximum number of bytes to write.
223      * @return the number of bytes written.
224      */
225     @BytesLong
write(@ytesLong long size)226     public long write(@BytesLong long size) {
227         return nativeWrite(size);
228     }
229 
230     /**
231      * Writes recording data to buffer.
232      *
233      * @param bytes the byte array stores the data to be written to DVR.
234      * @param offset the index of the first byte in {@code bytes} to be written to DVR.
235      * @param size the maximum number of bytes to write.
236      * @return the number of bytes written.
237      */
238     @BytesLong
write(@onNull byte[] bytes, @BytesLong long offset, @BytesLong long size)239     public long write(@NonNull byte[] bytes, @BytesLong long offset, @BytesLong long size) {
240         if (size + offset > bytes.length) {
241             throw new ArrayIndexOutOfBoundsException(
242                     "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size);
243         }
244         return nativeWrite(bytes, offset, size);
245     }
246 }
247