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