• 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 
setStatusCheckIntervalHint(int64_t)157 ::ndk::ScopedAStatus Dvr::setStatusCheckIntervalHint(int64_t /* in_milliseconds */) {
158     ALOGV("%s", __FUNCTION__);
159 
160     // There is no active polling in this default implementation,
161     // so directly return ok here.
162     return ::ndk::ScopedAStatus::ok();
163 }
164 
createDvrMQ()165 bool Dvr::createDvrMQ() {
166     ALOGV("%s", __FUNCTION__);
167 
168     // Create a synchronized FMQ that supports blocking read/write
169     unique_ptr<DvrMQ> tmpDvrMQ = unique_ptr<DvrMQ>(new (nothrow) DvrMQ(mBufferSize, true));
170     if (!tmpDvrMQ->isValid()) {
171         ALOGW("[Dvr] Failed to create FMQ of DVR");
172         return false;
173     }
174 
175     mDvrMQ = std::move(tmpDvrMQ);
176 
177     if (EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrEventFlag) != ::android::OK) {
178         return false;
179     }
180 
181     return true;
182 }
183 
getDvrEventFlag()184 EventFlag* Dvr::getDvrEventFlag() {
185     return mDvrEventFlag;
186 }
187 
dump(int fd,const char **,uint32_t)188 binder_status_t Dvr::dump(int fd, const char** /* args */, uint32_t /* numArgs */) {
189     dprintf(fd, "    Dvr:\n");
190     dprintf(fd, "      mType: %hhd\n", mType);
191     dprintf(fd, "      mDvrThreadRunning: %d\n", (bool)mDvrThreadRunning);
192     return STATUS_OK;
193 }
194 
playbackThreadLoop()195 void Dvr::playbackThreadLoop() {
196     ALOGD("[Dvr] playback threadLoop start.");
197 
198     while (mDvrThreadRunning) {
199         uint32_t efState = 0;
200         ::android::status_t status =
201                 mDvrEventFlag->wait(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY),
202                                     &efState, WAIT_TIMEOUT, true /* retry on spurious wake */);
203         if (status != ::android::OK) {
204             ALOGD("[Dvr] wait for data ready on the playback FMQ");
205             continue;
206         }
207 
208         // If the both dvr playback and dvr record are created, the playback will be treated as
209         // the source of the record. isVirtualFrontend set to true would direct the dvr playback
210         // input to the demux record filters or live broadcast filters.
211         bool isRecording = mDemux->isRecording();
212         bool isVirtualFrontend = isRecording;
213 
214         if (mDvrSettings.get<DvrSettings::Tag::playback>().dataFormat == DataFormat::ES) {
215             if (!processEsDataOnPlayback(isVirtualFrontend, isRecording)) {
216                 ALOGE("[Dvr] playback es data failed to be filtered. Ending thread");
217                 break;
218             }
219             maySendPlaybackStatusCallback();
220             continue;
221         }
222 
223         // Our current implementation filter the data and write it into the filter FMQ immediately
224         // after the DATA_READY from the VTS/framework
225         // This is for the non-ES data source, real playback use case handling.
226         if (!readPlaybackFMQ(isVirtualFrontend, isRecording) ||
227             !startFilterDispatcher(isVirtualFrontend, isRecording)) {
228             ALOGE("[Dvr] playback data failed to be filtered. Ending thread");
229             break;
230         }
231 
232         maySendPlaybackStatusCallback();
233     }
234 
235     mDvrThreadRunning = false;
236     ALOGD("[Dvr] playback thread ended.");
237 }
238 
maySendPlaybackStatusCallback()239 void Dvr::maySendPlaybackStatusCallback() {
240     lock_guard<mutex> lock(mPlaybackStatusLock);
241     int availableToRead = mDvrMQ->availableToRead();
242     int availableToWrite = mDvrMQ->availableToWrite();
243 
244     PlaybackStatus newStatus =
245             checkPlaybackStatusChange(availableToWrite, availableToRead,
246                                       mDvrSettings.get<DvrSettings::Tag::playback>().highThreshold,
247                                       mDvrSettings.get<DvrSettings::Tag::playback>().lowThreshold);
248     if (mPlaybackStatus != newStatus) {
249         mCallback->onPlaybackStatus(newStatus);
250         mPlaybackStatus = newStatus;
251     }
252 }
253 
checkPlaybackStatusChange(uint32_t availableToWrite,uint32_t availableToRead,int64_t highThreshold,int64_t lowThreshold)254 PlaybackStatus Dvr::checkPlaybackStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
255                                               int64_t highThreshold, int64_t lowThreshold) {
256     if (availableToWrite == 0) {
257         return PlaybackStatus::SPACE_FULL;
258     } else if (availableToRead > highThreshold) {
259         return PlaybackStatus::SPACE_ALMOST_FULL;
260     } else if (availableToRead < lowThreshold) {
261         return PlaybackStatus::SPACE_ALMOST_EMPTY;
262     } else if (availableToRead == 0) {
263         return PlaybackStatus::SPACE_EMPTY;
264     }
265     return mPlaybackStatus;
266 }
267 
readPlaybackFMQ(bool isVirtualFrontend,bool isRecording)268 bool Dvr::readPlaybackFMQ(bool isVirtualFrontend, bool isRecording) {
269     // Read playback data from the input FMQ
270     size_t size = mDvrMQ->availableToRead();
271     int64_t playbackPacketSize = mDvrSettings.get<DvrSettings::Tag::playback>().packetSize;
272     vector<int8_t> dataOutputBuffer;
273     dataOutputBuffer.resize(playbackPacketSize);
274     // Dispatch the packet to the PID matching filter output buffer
275     for (int i = 0; i < size / playbackPacketSize; i++) {
276         if (!mDvrMQ->read(dataOutputBuffer.data(), playbackPacketSize)) {
277             return false;
278         }
279         if (isVirtualFrontend) {
280             if (isRecording) {
281                 mDemux->sendFrontendInputToRecord(dataOutputBuffer);
282             } else {
283                 mDemux->startBroadcastTsFilter(dataOutputBuffer);
284             }
285         } else {
286             startTpidFilter(dataOutputBuffer);
287         }
288     }
289 
290     return true;
291 }
292 
processEsDataOnPlayback(bool isVirtualFrontend,bool isRecording)293 bool Dvr::processEsDataOnPlayback(bool isVirtualFrontend, bool isRecording) {
294     // Read ES from the DVR FMQ
295     // Note that currently we only provides ES with metaData in a specific format to be parsed.
296     // The ES size should be smaller than the Playback FMQ size to avoid reading truncated data.
297     int size = mDvrMQ->availableToRead();
298     vector<int8_t> dataOutputBuffer;
299     dataOutputBuffer.resize(size);
300     if (!mDvrMQ->read(dataOutputBuffer.data(), size)) {
301         return false;
302     }
303 
304     int metaDataSize = size;
305     int totalFrames = 0;
306     int videoEsDataSize = 0;
307     int audioEsDataSize = 0;
308     int audioPid = 0;
309     int videoPid = 0;
310 
311     vector<MediaEsMetaData> esMeta;
312     int videoReadPointer = 0;
313     int audioReadPointer = 0;
314     int frameCount = 0;
315     // Get meta data from the es
316     for (int i = 0; i < metaDataSize; i++) {
317         switch (dataOutputBuffer[i]) {
318             case 'm':
319                 metaDataSize = 0;
320                 getMetaDataValue(i, dataOutputBuffer.data(), metaDataSize);
321                 videoReadPointer = metaDataSize;
322                 continue;
323             case 'l':
324                 getMetaDataValue(i, dataOutputBuffer.data(), totalFrames);
325                 esMeta.resize(totalFrames);
326                 continue;
327             case 'V':
328                 getMetaDataValue(i, dataOutputBuffer.data(), videoEsDataSize);
329                 audioReadPointer = metaDataSize + videoEsDataSize;
330                 continue;
331             case 'A':
332                 getMetaDataValue(i, dataOutputBuffer.data(), audioEsDataSize);
333                 continue;
334             case 'p':
335                 if (dataOutputBuffer[++i] == 'a') {
336                     getMetaDataValue(i, dataOutputBuffer.data(), audioPid);
337                 } else if (dataOutputBuffer[i] == 'v') {
338                     getMetaDataValue(i, dataOutputBuffer.data(), videoPid);
339                 }
340                 continue;
341             case 'v':
342             case 'a':
343                 if (dataOutputBuffer[i + 1] != ',') {
344                     ALOGE("[Dvr] Invalid format meta data.");
345                     return false;
346                 }
347                 esMeta[frameCount] = {
348                         .isAudio = dataOutputBuffer[i] == 'a' ? true : false,
349                 };
350                 i += 5;  // Move to Len
351                 getMetaDataValue(i, dataOutputBuffer.data(), esMeta[frameCount].len);
352                 if (esMeta[frameCount].isAudio) {
353                     esMeta[frameCount].startIndex = audioReadPointer;
354                     audioReadPointer += esMeta[frameCount].len;
355                 } else {
356                     esMeta[frameCount].startIndex = videoReadPointer;
357                     videoReadPointer += esMeta[frameCount].len;
358                 }
359                 i += 4;  // move to PTS
360                 getMetaDataValue(i, dataOutputBuffer.data(), esMeta[frameCount].pts);
361                 frameCount++;
362                 continue;
363             default:
364                 continue;
365         }
366     }
367 
368     if (frameCount != totalFrames) {
369         ALOGE("[Dvr] Invalid meta data, frameCount=%d, totalFrames reported=%d", frameCount,
370               totalFrames);
371         return false;
372     }
373 
374     if (metaDataSize + audioEsDataSize + videoEsDataSize != size) {
375         ALOGE("[Dvr] Invalid meta data, metaSize=%d, videoSize=%d, audioSize=%d, totolSize=%d",
376               metaDataSize, videoEsDataSize, audioEsDataSize, size);
377         return false;
378     }
379 
380     // Read es raw data from the FMQ per meta data built previously
381     vector<int8_t> frameData;
382     map<int64_t, std::shared_ptr<IFilter>>::iterator it;
383     int pid = 0;
384     for (int i = 0; i < totalFrames; i++) {
385         frameData.resize(esMeta[i].len);
386         pid = esMeta[i].isAudio ? audioPid : videoPid;
387         memcpy(frameData.data(), dataOutputBuffer.data() + esMeta[i].startIndex, esMeta[i].len);
388         // Send to the media filters or record filters
389         if (!isRecording) {
390             for (it = mFilters.begin(); it != mFilters.end(); it++) {
391                 if (pid == mDemux->getFilterTpid(it->first)) {
392                     mDemux->updateMediaFilterOutput(it->first, frameData,
393                                                     static_cast<uint64_t>(esMeta[i].pts));
394                 }
395             }
396         } else {
397             mDemux->sendFrontendInputToRecord(frameData, pid, static_cast<uint64_t>(esMeta[i].pts));
398         }
399         startFilterDispatcher(isVirtualFrontend, isRecording);
400         frameData.clear();
401     }
402 
403     return true;
404 }
405 
getMetaDataValue(int & index,int8_t * dataOutputBuffer,int & value)406 void Dvr::getMetaDataValue(int& index, int8_t* dataOutputBuffer, int& value) {
407     index += 2;  // Move the pointer across the ":" to the value
408     while (dataOutputBuffer[index] != ',' && dataOutputBuffer[index] != '\n') {
409         value = ((dataOutputBuffer[index++] - 48) + value * 10);
410     }
411 }
412 
startTpidFilter(vector<int8_t> data)413 void Dvr::startTpidFilter(vector<int8_t> data) {
414     map<int64_t, std::shared_ptr<IFilter>>::iterator it;
415     for (it = mFilters.begin(); it != mFilters.end(); it++) {
416         uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff));
417         if (DEBUG_DVR) {
418             ALOGW("[Dvr] start ts filter pid: %d", pid);
419         }
420         if (pid == mDemux->getFilterTpid(it->first)) {
421             mDemux->updateFilterOutput(it->first, data);
422         }
423     }
424 }
425 
startFilterDispatcher(bool isVirtualFrontend,bool isRecording)426 bool Dvr::startFilterDispatcher(bool isVirtualFrontend, bool isRecording) {
427     if (isVirtualFrontend) {
428         if (isRecording) {
429             return mDemux->startRecordFilterDispatcher();
430         } else {
431             return mDemux->startBroadcastFilterDispatcher();
432         }
433     }
434 
435     map<int64_t, std::shared_ptr<IFilter>>::iterator it;
436     // Handle the output data per filter type
437     for (it = mFilters.begin(); it != mFilters.end(); it++) {
438         if (!mDemux->startFilterHandler(it->first).isOk()) {
439             return false;
440         }
441     }
442 
443     return true;
444 }
445 
writeRecordFMQ(const vector<int8_t> & data)446 bool Dvr::writeRecordFMQ(const vector<int8_t>& data) {
447     lock_guard<mutex> lock(mWriteLock);
448     if (mRecordStatus == RecordStatus::OVERFLOW) {
449         ALOGW("[Dvr] stops writing and wait for the client side flushing.");
450         return true;
451     }
452     if (mDvrMQ->write(data.data(), data.size())) {
453         mDvrEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
454         maySendRecordStatusCallback();
455         return true;
456     }
457 
458     maySendRecordStatusCallback();
459     return false;
460 }
461 
maySendRecordStatusCallback()462 void Dvr::maySendRecordStatusCallback() {
463     lock_guard<mutex> lock(mRecordStatusLock);
464     int availableToRead = mDvrMQ->availableToRead();
465     int availableToWrite = mDvrMQ->availableToWrite();
466 
467     RecordStatus newStatus =
468             checkRecordStatusChange(availableToWrite, availableToRead,
469                                     mDvrSettings.get<DvrSettings::Tag::record>().highThreshold,
470                                     mDvrSettings.get<DvrSettings::Tag::record>().lowThreshold);
471     if (mRecordStatus != newStatus) {
472         mCallback->onRecordStatus(newStatus);
473         mRecordStatus = newStatus;
474     }
475 }
476 
checkRecordStatusChange(uint32_t availableToWrite,uint32_t availableToRead,int64_t highThreshold,int64_t lowThreshold)477 RecordStatus Dvr::checkRecordStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
478                                           int64_t highThreshold, int64_t lowThreshold) {
479     if (availableToWrite == 0) {
480         return RecordStatus::OVERFLOW;
481     } else if (availableToRead > highThreshold) {
482         return RecordStatus::HIGH_WATER;
483     } else if (availableToRead < lowThreshold) {
484         return RecordStatus::LOW_WATER;
485     }
486     return mRecordStatus;
487 }
488 
addPlaybackFilter(int64_t filterId,std::shared_ptr<IFilter> filter)489 bool Dvr::addPlaybackFilter(int64_t filterId, std::shared_ptr<IFilter> filter) {
490     mFilters[filterId] = filter;
491     return true;
492 }
493 
removePlaybackFilter(int64_t filterId)494 bool Dvr::removePlaybackFilter(int64_t filterId) {
495     mFilters.erase(filterId);
496     return true;
497 }
498 
499 }  // namespace tuner
500 }  // namespace tv
501 }  // namespace hardware
502 }  // namespace android
503 }  // namespace aidl
504