1 /*
2 * Copyright (C) 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 #define LOG_TAG "android.hardware.tv.tuner@1.0-Dvr"
18
19 #include "Dvr.h"
20 #include <utils/Log.h>
21
22 namespace android {
23 namespace hardware {
24 namespace tv {
25 namespace tuner {
26 namespace V1_0 {
27 namespace implementation {
28
29 #define WAIT_TIMEOUT 3000000000
30
Dvr()31 Dvr::Dvr() {}
32
Dvr(DvrType type,uint32_t bufferSize,const sp<IDvrCallback> & cb,sp<Demux> demux)33 Dvr::Dvr(DvrType type, uint32_t bufferSize, const sp<IDvrCallback>& cb, sp<Demux> demux) {
34 mType = type;
35 mBufferSize = bufferSize;
36 mCallback = cb;
37 mDemux = demux;
38 }
39
~Dvr()40 Dvr::~Dvr() {}
41
getQueueDesc(getQueueDesc_cb _hidl_cb)42 Return<void> Dvr::getQueueDesc(getQueueDesc_cb _hidl_cb) {
43 ALOGV("%s", __FUNCTION__);
44
45 _hidl_cb(Result::SUCCESS, *mDvrMQ->getDesc());
46 return Void();
47 }
48
configure(const DvrSettings & settings)49 Return<Result> Dvr::configure(const DvrSettings& settings) {
50 ALOGV("%s", __FUNCTION__);
51
52 mDvrSettings = settings;
53 mDvrConfigured = true;
54
55 return Result::SUCCESS;
56 }
57
attachFilter(const sp<IFilter> & filter)58 Return<Result> Dvr::attachFilter(const sp<IFilter>& filter) {
59 ALOGV("%s", __FUNCTION__);
60
61 uint32_t filterId;
62 Result status;
63
64 filter->getId([&](Result result, uint32_t id) {
65 filterId = id;
66 status = result;
67 });
68
69 if (status != Result::SUCCESS) {
70 return status;
71 }
72
73 // check if the attached filter is a record filter
74
75 mFilters[filterId] = filter;
76 mIsRecordFilterAttached = true;
77 if (!mDemux->attachRecordFilter(filterId)) {
78 return Result::INVALID_ARGUMENT;
79 }
80 mDemux->setIsRecording(mIsRecordStarted | mIsRecordFilterAttached);
81
82 return Result::SUCCESS;
83 }
84
detachFilter(const sp<IFilter> & filter)85 Return<Result> Dvr::detachFilter(const sp<IFilter>& filter) {
86 ALOGV("%s", __FUNCTION__);
87
88 uint32_t filterId;
89 Result status;
90
91 filter->getId([&](Result result, uint32_t id) {
92 filterId = id;
93 status = result;
94 });
95
96 if (status != Result::SUCCESS) {
97 return status;
98 }
99
100 std::map<uint32_t, sp<IFilter>>::iterator it;
101
102 it = mFilters.find(filterId);
103 if (it != mFilters.end()) {
104 mFilters.erase(filterId);
105 if (!mDemux->detachRecordFilter(filterId)) {
106 return Result::INVALID_ARGUMENT;
107 }
108 }
109
110 // If all the filters are detached, record can't be started
111 if (mFilters.empty()) {
112 mIsRecordFilterAttached = false;
113 mDemux->setIsRecording(mIsRecordStarted | mIsRecordFilterAttached);
114 }
115
116 return Result::SUCCESS;
117 }
118
start()119 Return<Result> Dvr::start() {
120 ALOGV("%s", __FUNCTION__);
121
122 if (!mCallback) {
123 return Result::NOT_INITIALIZED;
124 }
125
126 if (!mDvrConfigured) {
127 return Result::INVALID_STATE;
128 }
129
130 if (mType == DvrType::PLAYBACK) {
131 pthread_create(&mDvrThread, NULL, __threadLoopPlayback, this);
132 pthread_setname_np(mDvrThread, "playback_waiting_loop");
133 } else if (mType == DvrType::RECORD) {
134 mRecordStatus = RecordStatus::DATA_READY;
135 mIsRecordStarted = true;
136 mDemux->setIsRecording(mIsRecordStarted | mIsRecordFilterAttached);
137 }
138
139 // TODO start another thread to send filter status callback to the framework
140
141 return Result::SUCCESS;
142 }
143
stop()144 Return<Result> Dvr::stop() {
145 ALOGV("%s", __FUNCTION__);
146
147 mDvrThreadRunning = false;
148
149 std::lock_guard<std::mutex> lock(mDvrThreadLock);
150
151 mIsRecordStarted = false;
152 mDemux->setIsRecording(mIsRecordStarted | mIsRecordFilterAttached);
153
154 return Result::SUCCESS;
155 }
156
flush()157 Return<Result> Dvr::flush() {
158 ALOGV("%s", __FUNCTION__);
159
160 mRecordStatus = RecordStatus::DATA_READY;
161
162 return Result::SUCCESS;
163 }
164
close()165 Return<Result> Dvr::close() {
166 ALOGV("%s", __FUNCTION__);
167
168 return Result::SUCCESS;
169 }
170
createDvrMQ()171 bool Dvr::createDvrMQ() {
172 ALOGV("%s", __FUNCTION__);
173
174 // Create a synchronized FMQ that supports blocking read/write
175 std::unique_ptr<DvrMQ> tmpDvrMQ =
176 std::unique_ptr<DvrMQ>(new (std::nothrow) DvrMQ(mBufferSize, true));
177 if (!tmpDvrMQ->isValid()) {
178 ALOGW("Failed to create FMQ of DVR");
179 return false;
180 }
181
182 mDvrMQ = std::move(tmpDvrMQ);
183
184 if (EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrEventFlag) != OK) {
185 return false;
186 }
187
188 return true;
189 }
190
__threadLoopPlayback(void * user)191 void* Dvr::__threadLoopPlayback(void* user) {
192 Dvr* const self = static_cast<Dvr*>(user);
193 self->playbackThreadLoop();
194 return 0;
195 }
196
playbackThreadLoop()197 void Dvr::playbackThreadLoop() {
198 ALOGD("[Dvr] playback threadLoop start.");
199 std::lock_guard<std::mutex> lock(mDvrThreadLock);
200 mDvrThreadRunning = true;
201
202 while (mDvrThreadRunning) {
203 uint32_t efState = 0;
204 status_t status =
205 mDvrEventFlag->wait(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY),
206 &efState, WAIT_TIMEOUT, true /* retry on spurious wake */);
207 if (status != OK) {
208 ALOGD("[Dvr] wait for data ready on the playback FMQ");
209 continue;
210 }
211 // Our current implementation filter the data and write it into the filter FMQ immediately
212 // after the DATA_READY from the VTS/framework
213 if (!readPlaybackFMQ() || !startFilterDispatcher()) {
214 ALOGD("[Dvr] playback data failed to be filtered. Ending thread");
215 break;
216 }
217
218 maySendPlaybackStatusCallback();
219 }
220
221 mDvrThreadRunning = false;
222 ALOGD("[Dvr] playback thread ended.");
223 }
224
maySendPlaybackStatusCallback()225 void Dvr::maySendPlaybackStatusCallback() {
226 std::lock_guard<std::mutex> lock(mPlaybackStatusLock);
227 int availableToRead = mDvrMQ->availableToRead();
228 int availableToWrite = mDvrMQ->availableToWrite();
229
230 PlaybackStatus newStatus = checkPlaybackStatusChange(availableToWrite, availableToRead,
231 mDvrSettings.playback().highThreshold,
232 mDvrSettings.playback().lowThreshold);
233 if (mPlaybackStatus != newStatus) {
234 mCallback->onPlaybackStatus(newStatus);
235 mPlaybackStatus = newStatus;
236 }
237 }
238
checkPlaybackStatusChange(uint32_t availableToWrite,uint32_t availableToRead,uint32_t highThreshold,uint32_t lowThreshold)239 PlaybackStatus Dvr::checkPlaybackStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
240 uint32_t highThreshold, uint32_t lowThreshold) {
241 if (availableToWrite == 0) {
242 return PlaybackStatus::SPACE_FULL;
243 } else if (availableToRead > highThreshold) {
244 return PlaybackStatus::SPACE_ALMOST_FULL;
245 } else if (availableToRead < lowThreshold) {
246 return PlaybackStatus::SPACE_ALMOST_EMPTY;
247 } else if (availableToRead == 0) {
248 return PlaybackStatus::SPACE_EMPTY;
249 }
250 return mPlaybackStatus;
251 }
252
readPlaybackFMQ()253 bool Dvr::readPlaybackFMQ() {
254 // Read playback data from the input FMQ
255 int size = mDvrMQ->availableToRead();
256 int playbackPacketSize = mDvrSettings.playback().packetSize;
257 vector<uint8_t> dataOutputBuffer;
258 dataOutputBuffer.resize(playbackPacketSize);
259
260 // Dispatch the packet to the PID matching filter output buffer
261 for (int i = 0; i < size / playbackPacketSize; i++) {
262 if (!mDvrMQ->read(dataOutputBuffer.data(), playbackPacketSize)) {
263 return false;
264 }
265 startTpidFilter(dataOutputBuffer);
266 }
267
268 return true;
269 }
270
startTpidFilter(vector<uint8_t> data)271 void Dvr::startTpidFilter(vector<uint8_t> data) {
272 std::map<uint32_t, sp<IFilter>>::iterator it;
273 for (it = mFilters.begin(); it != mFilters.end(); it++) {
274 uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff));
275 if (DEBUG_DVR) {
276 ALOGW("[Dvr] start ts filter pid: %d", pid);
277 }
278 if (pid == mDemux->getFilterTpid(it->first)) {
279 mDemux->updateFilterOutput(it->first, data);
280 }
281 }
282 }
283
startFilterDispatcher()284 bool Dvr::startFilterDispatcher() {
285 std::map<uint32_t, sp<IFilter>>::iterator it;
286
287 // Handle the output data per filter type
288 for (it = mFilters.begin(); it != mFilters.end(); it++) {
289 if (mDemux->startFilterHandler(it->first) != Result::SUCCESS) {
290 return false;
291 }
292 }
293
294 return true;
295 }
296
writeRecordFMQ(const std::vector<uint8_t> & data)297 bool Dvr::writeRecordFMQ(const std::vector<uint8_t>& data) {
298 std::lock_guard<std::mutex> lock(mWriteLock);
299 ALOGW("[Dvr] write record FMQ");
300 if (mDvrMQ->write(data.data(), data.size())) {
301 mDvrEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
302 maySendRecordStatusCallback();
303 return true;
304 }
305
306 maySendRecordStatusCallback();
307 return false;
308 }
309
maySendRecordStatusCallback()310 void Dvr::maySendRecordStatusCallback() {
311 std::lock_guard<std::mutex> lock(mRecordStatusLock);
312 int availableToRead = mDvrMQ->availableToRead();
313 int availableToWrite = mDvrMQ->availableToWrite();
314
315 RecordStatus newStatus = checkRecordStatusChange(availableToWrite, availableToRead,
316 mDvrSettings.record().highThreshold,
317 mDvrSettings.record().lowThreshold);
318 if (mRecordStatus != newStatus) {
319 mCallback->onRecordStatus(newStatus);
320 mRecordStatus = newStatus;
321 }
322 }
323
checkRecordStatusChange(uint32_t availableToWrite,uint32_t availableToRead,uint32_t highThreshold,uint32_t lowThreshold)324 RecordStatus Dvr::checkRecordStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
325 uint32_t highThreshold, uint32_t lowThreshold) {
326 if (availableToWrite == 0) {
327 return DemuxFilterStatus::OVERFLOW;
328 } else if (availableToRead > highThreshold) {
329 return DemuxFilterStatus::HIGH_WATER;
330 } else if (availableToRead < lowThreshold) {
331 return DemuxFilterStatus::LOW_WATER;
332 }
333 return mRecordStatus;
334 }
335
336 } // namespace implementation
337 } // namespace V1_0
338 } // namespace tuner
339 } // namespace tv
340 } // namespace hardware
341 } // namespace android