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