xref: /aosp_15_r20/hardware/interfaces/tv/tuner/1.0/default/Dvr.cpp (revision 4d7e907c777eeecc4c5bd7cf640a754fac206ff7)
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 "[email protected]"
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     // make sure thread has joined
42     close();
43 }
44 
getQueueDesc(getQueueDesc_cb _hidl_cb)45 Return<void> Dvr::getQueueDesc(getQueueDesc_cb _hidl_cb) {
46     ALOGV("%s", __FUNCTION__);
47 
48     _hidl_cb(Result::SUCCESS, *mDvrMQ->getDesc());
49     return Void();
50 }
51 
configure(const DvrSettings & settings)52 Return<Result> Dvr::configure(const DvrSettings& settings) {
53     ALOGV("%s", __FUNCTION__);
54 
55     mDvrSettings = settings;
56     mDvrConfigured = true;
57 
58     return Result::SUCCESS;
59 }
60 
attachFilter(const sp<IFilter> & filter)61 Return<Result> Dvr::attachFilter(const sp<IFilter>& filter) {
62     ALOGV("%s", __FUNCTION__);
63 
64     uint32_t filterId;
65     Result status;
66 
67     filter->getId([&](Result result, uint32_t id) {
68         filterId = id;
69         status = result;
70     });
71 
72     if (status != Result::SUCCESS) {
73         return status;
74     }
75 
76     // TODO check if the attached filter is a record filter
77     if (!mDemux->attachRecordFilter(filterId)) {
78         return Result::INVALID_ARGUMENT;
79     }
80 
81     return Result::SUCCESS;
82 }
83 
detachFilter(const sp<IFilter> & filter)84 Return<Result> Dvr::detachFilter(const sp<IFilter>& filter) {
85     ALOGV("%s", __FUNCTION__);
86 
87     uint32_t filterId;
88     Result status;
89 
90     filter->getId([&](Result result, uint32_t id) {
91         filterId = id;
92         status = result;
93     });
94 
95     if (status != Result::SUCCESS) {
96         return status;
97     }
98 
99     if (!mDemux->detachRecordFilter(filterId)) {
100         return Result::INVALID_ARGUMENT;
101     }
102 
103     return Result::SUCCESS;
104 }
105 
start()106 Return<Result> Dvr::start() {
107     ALOGV("%s", __FUNCTION__);
108 
109     if (!mCallback) {
110         return Result::NOT_INITIALIZED;
111     }
112 
113     if (!mDvrConfigured) {
114         return Result::INVALID_STATE;
115     }
116 
117     if (mType == DvrType::PLAYBACK) {
118         mDvrThread = std::thread(&Dvr::playbackThreadLoop, this);
119     } else if (mType == DvrType::RECORD) {
120         mRecordStatus = RecordStatus::DATA_READY;
121         mDemux->setIsRecording(mType == DvrType::RECORD);
122     }
123 
124     // TODO start another thread to send filter status callback to the framework
125 
126     return Result::SUCCESS;
127 }
128 
stop()129 Return<Result> Dvr::stop() {
130     ALOGV("%s", __FUNCTION__);
131 
132     mDvrThreadRunning = false;
133     if (mDvrThread.joinable()) {
134         mDvrThread.join();
135     }
136     // thread should always be joinable if it is running,
137     // so it should be safe to assume recording stopped.
138     mDemux->setIsRecording(false);
139 
140     return Result::SUCCESS;
141 }
142 
flush()143 Return<Result> Dvr::flush() {
144     ALOGV("%s", __FUNCTION__);
145 
146     mRecordStatus = RecordStatus::DATA_READY;
147 
148     return Result::SUCCESS;
149 }
150 
close()151 Return<Result> Dvr::close() {
152     ALOGV("%s", __FUNCTION__);
153     stop();
154     return Result::SUCCESS;
155 }
156 
createDvrMQ()157 bool Dvr::createDvrMQ() {
158     ALOGV("%s", __FUNCTION__);
159 
160     // Create a synchronized FMQ that supports blocking read/write
161     unique_ptr<DvrMQ> tmpDvrMQ = unique_ptr<DvrMQ>(new (nothrow) DvrMQ(mBufferSize, true));
162     if (!tmpDvrMQ->isValid()) {
163         ALOGW("[Dvr] Failed to create FMQ of DVR");
164         return false;
165     }
166 
167     mDvrMQ = std::move(tmpDvrMQ);
168 
169     if (EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrEventFlag) != OK) {
170         return false;
171     }
172 
173     return true;
174 }
175 
getDvrEventFlag()176 EventFlag* Dvr::getDvrEventFlag() {
177     return mDvrEventFlag;
178 }
179 
playbackThreadLoop()180 void Dvr::playbackThreadLoop() {
181     ALOGD("[Dvr] playback threadLoop start.");
182     mDvrThreadRunning = true;
183 
184     while (mDvrThreadRunning) {
185         uint32_t efState = 0;
186         status_t status =
187                 mDvrEventFlag->wait(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY),
188                                     &efState, WAIT_TIMEOUT, true /* retry on spurious wake */);
189         if (status != OK) {
190             ALOGD("[Dvr] wait for data ready on the playback FMQ");
191             continue;
192         }
193 
194         if (mDvrSettings.playback().dataFormat == DataFormat::ES) {
195             if (!processEsDataOnPlayback(false /*isVirtualFrontend*/, false /*isRecording*/)) {
196                 ALOGE("[Dvr] playback es data failed to be filtered. Ending thread");
197                 break;
198             }
199             maySendPlaybackStatusCallback();
200         }
201         // Our current implementation filter the data and write it into the filter FMQ immediately
202         // after the DATA_READY from the VTS/framework
203         if (!readPlaybackFMQ(false /*isVirtualFrontend*/, false /*isRecording*/) ||
204             !startFilterDispatcher(false /*isVirtualFrontend*/, false /*isRecording*/)) {
205             ALOGE("[Dvr] playback data failed to be filtered. Ending thread");
206             break;
207         }
208 
209         maySendPlaybackStatusCallback();
210     }
211 
212     mDvrThreadRunning = false;
213     ALOGD("[Dvr] playback thread ended.");
214 }
215 
maySendPlaybackStatusCallback()216 void Dvr::maySendPlaybackStatusCallback() {
217     lock_guard<mutex> lock(mPlaybackStatusLock);
218     int availableToRead = mDvrMQ->availableToRead();
219     int availableToWrite = mDvrMQ->availableToWrite();
220 
221     PlaybackStatus newStatus = checkPlaybackStatusChange(availableToWrite, availableToRead,
222                                                          mDvrSettings.playback().highThreshold,
223                                                          mDvrSettings.playback().lowThreshold);
224     if (mPlaybackStatus != newStatus) {
225         mCallback->onPlaybackStatus(newStatus);
226         mPlaybackStatus = newStatus;
227     }
228 }
229 
checkPlaybackStatusChange(uint32_t availableToWrite,uint32_t availableToRead,uint32_t highThreshold,uint32_t lowThreshold)230 PlaybackStatus Dvr::checkPlaybackStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
231                                               uint32_t highThreshold, uint32_t lowThreshold) {
232     if (availableToWrite == 0) {
233         return PlaybackStatus::SPACE_FULL;
234     } else if (availableToRead > highThreshold) {
235         return PlaybackStatus::SPACE_ALMOST_FULL;
236     } else if (availableToRead < lowThreshold) {
237         return PlaybackStatus::SPACE_ALMOST_EMPTY;
238     } else if (availableToRead == 0) {
239         return PlaybackStatus::SPACE_EMPTY;
240     }
241     return mPlaybackStatus;
242 }
243 
readPlaybackFMQ(bool isVirtualFrontend,bool isRecording)244 bool Dvr::readPlaybackFMQ(bool isVirtualFrontend, bool isRecording) {
245     // Read playback data from the input FMQ
246     int size = mDvrMQ->availableToRead();
247     int playbackPacketSize = mDvrSettings.playback().packetSize;
248     vector<uint8_t> dataOutputBuffer;
249     dataOutputBuffer.resize(playbackPacketSize);
250     // Dispatch the packet to the PID matching filter output buffer
251     for (int i = 0; i < size / playbackPacketSize; i++) {
252         if (!mDvrMQ->read(dataOutputBuffer.data(), playbackPacketSize)) {
253             return false;
254         }
255         if (isVirtualFrontend) {
256             if (isRecording) {
257                 mDemux->sendFrontendInputToRecord(dataOutputBuffer);
258             } else {
259                 mDemux->startBroadcastTsFilter(dataOutputBuffer);
260             }
261         } else {
262             startTpidFilter(dataOutputBuffer);
263         }
264     }
265 
266     return true;
267 }
268 
processEsDataOnPlayback(bool isVirtualFrontend,bool isRecording)269 bool Dvr::processEsDataOnPlayback(bool isVirtualFrontend, bool isRecording) {
270     // Read ES from the DVR FMQ
271     // Note that currently we only provides ES with metaData in a specific format to be parsed.
272     // The ES size should be smaller than the Playback FMQ size to avoid reading truncated data.
273     int size = mDvrMQ->availableToRead();
274     vector<uint8_t> dataOutputBuffer;
275     dataOutputBuffer.resize(size);
276     if (!mDvrMQ->read(dataOutputBuffer.data(), size)) {
277         return false;
278     }
279 
280     int metaDataSize = size;
281     int totalFrames = 0;
282     int videoEsDataSize = 0;
283     int audioEsDataSize = 0;
284     int audioPid = 0;
285     int videoPid = 0;
286 
287     vector<MediaEsMetaData> esMeta;
288     int videoReadPointer = 0;
289     int audioReadPointer = 0;
290     int frameCount = 0;
291     // Get meta data from the es
292     for (int i = 0; i < metaDataSize; i++) {
293         switch (dataOutputBuffer[i]) {
294             case 'm':
295                 metaDataSize = 0;
296                 getMetaDataValue(i, dataOutputBuffer.data(), metaDataSize);
297                 videoReadPointer = metaDataSize;
298                 continue;
299             case 'l':
300                 getMetaDataValue(i, dataOutputBuffer.data(), totalFrames);
301                 esMeta.resize(totalFrames);
302                 continue;
303             case 'V':
304                 getMetaDataValue(i, dataOutputBuffer.data(), videoEsDataSize);
305                 audioReadPointer = metaDataSize + videoEsDataSize;
306                 continue;
307             case 'A':
308                 getMetaDataValue(i, dataOutputBuffer.data(), audioEsDataSize);
309                 continue;
310             case 'p':
311                 if (dataOutputBuffer[++i] == 'a') {
312                     getMetaDataValue(i, dataOutputBuffer.data(), audioPid);
313                 } else if (dataOutputBuffer[i] == 'v') {
314                     getMetaDataValue(i, dataOutputBuffer.data(), videoPid);
315                 }
316                 continue;
317             case 'v':
318             case 'a':
319                 if (dataOutputBuffer[i + 1] != ',') {
320                     ALOGE("[Dvr] Invalid format meta data.");
321                     return false;
322                 }
323                 esMeta[frameCount] = {
324                         .isAudio = dataOutputBuffer[i] == 'a' ? true : false,
325                 };
326                 i += 5;  // Move to Len
327                 getMetaDataValue(i, dataOutputBuffer.data(), esMeta[frameCount].len);
328                 if (esMeta[frameCount].isAudio) {
329                     esMeta[frameCount].startIndex = audioReadPointer;
330                     audioReadPointer += esMeta[frameCount].len;
331                 } else {
332                     esMeta[frameCount].startIndex = videoReadPointer;
333                     videoReadPointer += esMeta[frameCount].len;
334                 }
335                 i += 4;  // move to PTS
336                 getMetaDataValue(i, dataOutputBuffer.data(), esMeta[frameCount].pts);
337                 frameCount++;
338                 continue;
339             default:
340                 continue;
341         }
342     }
343 
344     if (frameCount != totalFrames) {
345         ALOGE("[Dvr] Invalid meta data, frameCount=%d, totalFrames reported=%d", frameCount,
346               totalFrames);
347         return false;
348     }
349 
350     if (metaDataSize + audioEsDataSize + videoEsDataSize != size) {
351         ALOGE("[Dvr] Invalid meta data, metaSize=%d, videoSize=%d, audioSize=%d, totolSize=%d",
352               metaDataSize, videoEsDataSize, audioEsDataSize, size);
353         return false;
354     }
355 
356     // Read es raw data from the FMQ per meta data built previously
357     vector<uint8_t> frameData;
358     map<uint32_t, sp<IFilter>>::iterator it;
359     int pid = 0;
360     for (int i = 0; i < totalFrames; i++) {
361         frameData.resize(esMeta[i].len);
362         pid = esMeta[i].isAudio ? audioPid : videoPid;
363         memcpy(frameData.data(), dataOutputBuffer.data() + esMeta[i].startIndex, esMeta[i].len);
364         // Send to the media filter
365         if (isVirtualFrontend && isRecording) {
366             // TODO validate record
367             mDemux->sendFrontendInputToRecord(frameData);
368         } else {
369             for (it = mFilters.begin(); it != mFilters.end(); it++) {
370                 if (pid == mDemux->getFilterTpid(it->first)) {
371                     mDemux->updateMediaFilterOutput(it->first, frameData,
372                                                     static_cast<uint64_t>(esMeta[i].pts));
373                     startFilterDispatcher(isVirtualFrontend, isRecording);
374                 }
375             }
376         }
377     }
378 
379     return true;
380 }
381 
getMetaDataValue(int & index,uint8_t * dataOutputBuffer,int & value)382 void Dvr::getMetaDataValue(int& index, uint8_t* dataOutputBuffer, int& value) {
383     index += 2;  // Move the pointer across the ":" to the value
384     while (dataOutputBuffer[index] != ',' && dataOutputBuffer[index] != '\n') {
385         value = ((dataOutputBuffer[index++] - 48) + value * 10);
386     }
387 }
388 
startTpidFilter(vector<uint8_t> data)389 void Dvr::startTpidFilter(vector<uint8_t> data) {
390     map<uint32_t, sp<IFilter>>::iterator it;
391     for (it = mFilters.begin(); it != mFilters.end(); it++) {
392         uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff));
393         if (DEBUG_DVR) {
394             ALOGW("[Dvr] start ts filter pid: %d", pid);
395         }
396         if (pid == mDemux->getFilterTpid(it->first)) {
397             mDemux->updateFilterOutput(it->first, data);
398         }
399     }
400 }
401 
startFilterDispatcher(bool isVirtualFrontend,bool isRecording)402 bool Dvr::startFilterDispatcher(bool isVirtualFrontend, bool isRecording) {
403     if (isVirtualFrontend) {
404         if (isRecording) {
405             return mDemux->startRecordFilterDispatcher();
406         } else {
407             return mDemux->startBroadcastFilterDispatcher();
408         }
409     }
410 
411     map<uint32_t, sp<IFilter>>::iterator it;
412     // Handle the output data per filter type
413     for (it = mFilters.begin(); it != mFilters.end(); it++) {
414         if (mDemux->startFilterHandler(it->first) != Result::SUCCESS) {
415             return false;
416         }
417     }
418 
419     return true;
420 }
421 
writeRecordFMQ(const vector<uint8_t> & data)422 bool Dvr::writeRecordFMQ(const vector<uint8_t>& data) {
423     lock_guard<mutex> lock(mWriteLock);
424     if (mRecordStatus == RecordStatus::OVERFLOW) {
425         ALOGW("[Dvr] stops writing and wait for the client side flushing.");
426         return true;
427     }
428     if (mDvrMQ->write(data.data(), data.size())) {
429         mDvrEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
430         maySendRecordStatusCallback();
431         return true;
432     }
433 
434     maySendRecordStatusCallback();
435     return false;
436 }
437 
maySendRecordStatusCallback()438 void Dvr::maySendRecordStatusCallback() {
439     lock_guard<mutex> lock(mRecordStatusLock);
440     int availableToRead = mDvrMQ->availableToRead();
441     int availableToWrite = mDvrMQ->availableToWrite();
442 
443     RecordStatus newStatus = checkRecordStatusChange(availableToWrite, availableToRead,
444                                                      mDvrSettings.record().highThreshold,
445                                                      mDvrSettings.record().lowThreshold);
446     if (mRecordStatus != newStatus) {
447         mCallback->onRecordStatus(newStatus);
448         mRecordStatus = newStatus;
449     }
450 }
451 
checkRecordStatusChange(uint32_t availableToWrite,uint32_t availableToRead,uint32_t highThreshold,uint32_t lowThreshold)452 RecordStatus Dvr::checkRecordStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
453                                           uint32_t highThreshold, uint32_t lowThreshold) {
454     if (availableToWrite == 0) {
455         return DemuxFilterStatus::OVERFLOW;
456     } else if (availableToRead > highThreshold) {
457         return DemuxFilterStatus::HIGH_WATER;
458     } else if (availableToRead < lowThreshold) {
459         return DemuxFilterStatus::LOW_WATER;
460     }
461     return mRecordStatus;
462 }
463 
addPlaybackFilter(uint32_t filterId,sp<IFilter> filter)464 bool Dvr::addPlaybackFilter(uint32_t filterId, sp<IFilter> filter) {
465     mFilters[filterId] = filter;
466     return true;
467 }
468 
removePlaybackFilter(uint32_t filterId)469 bool Dvr::removePlaybackFilter(uint32_t filterId) {
470     mFilters.erase(filterId);
471     return true;
472 }
473 }  // namespace implementation
474 }  // namespace V1_0
475 }  // namespace tuner
476 }  // namespace tv
477 }  // namespace hardware
478 }  // namespace android
479