blob: c487d98c36989c7066d845bdec139769ca4b632f [file] [log] [blame]
Amy Zhangbb94eeb2020-07-09 22:48:04 -07001/*
2 * Copyright 2020 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.1-Dvr"
18
19#include "Dvr.h"
20#include <utils/Log.h>
21
22namespace android {
23namespace hardware {
24namespace tv {
25namespace tuner {
26namespace V1_0 {
27namespace implementation {
28
29#define WAIT_TIMEOUT 3000000000
30
31Dvr::Dvr() {}
32
33Dvr::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
Amy Zhang9d5289f2021-02-18 13:56:45 -080040Dvr::~Dvr() {
41 mDvrThreadRunning = false;
42 lock_guard<mutex> lock(mDvrThreadLock);
43}
Amy Zhangbb94eeb2020-07-09 22:48:04 -070044
45Return<void> Dvr::getQueueDesc(getQueueDesc_cb _hidl_cb) {
46 ALOGV("%s", __FUNCTION__);
47
48 _hidl_cb(Result::SUCCESS, *mDvrMQ->getDesc());
49 return Void();
50}
51
52Return<Result> Dvr::configure(const DvrSettings& settings) {
53 ALOGV("%s", __FUNCTION__);
54
55 mDvrSettings = settings;
56 mDvrConfigured = true;
57
58 return Result::SUCCESS;
59}
60
61Return<Result> Dvr::attachFilter(const sp<V1_0::IFilter>& filter) {
62 ALOGV("%s", __FUNCTION__);
63
64 uint64_t filterId;
65 Result status;
66
67 sp<V1_1::IFilter> filter_v1_1 = V1_1::IFilter::castFrom(filter);
68 if (filter_v1_1 != NULL) {
69 filter_v1_1->getId64Bit([&](Result result, uint64_t id) {
70 filterId = id;
71 status = result;
72 });
73 } else {
74 filter->getId([&](Result result, uint32_t id) {
75 filterId = id;
76 status = result;
77 });
78 }
79
80 if (status != Result::SUCCESS) {
81 return status;
82 }
83
84 // TODO check if the attached filter is a record filter
85 if (!mDemux->attachRecordFilter(filterId)) {
86 return Result::INVALID_ARGUMENT;
87 }
88
89 return Result::SUCCESS;
90}
91
92Return<Result> Dvr::detachFilter(const sp<V1_0::IFilter>& filter) {
93 ALOGV("%s", __FUNCTION__);
94
95 uint64_t filterId;
96 Result status;
97
98 sp<V1_1::IFilter> filter_v1_1 = V1_1::IFilter::castFrom(filter);
99 if (filter_v1_1 != NULL) {
100 filter_v1_1->getId64Bit([&](Result result, uint64_t id) {
101 filterId = id;
102 status = result;
103 });
104 } else {
105 filter->getId([&](Result result, uint32_t id) {
106 filterId = id;
107 status = result;
108 });
109 }
110
111 if (status != Result::SUCCESS) {
112 return status;
113 }
114
115 if (!mDemux->detachRecordFilter(filterId)) {
116 return Result::INVALID_ARGUMENT;
117 }
118
119 return Result::SUCCESS;
120}
121
122Return<Result> Dvr::start() {
123 ALOGV("%s", __FUNCTION__);
Amy Zhang9d5289f2021-02-18 13:56:45 -0800124 if (mDvrThreadRunning) {
125 return Result::SUCCESS;
126 }
Amy Zhangbb94eeb2020-07-09 22:48:04 -0700127
128 if (!mCallback) {
129 return Result::NOT_INITIALIZED;
130 }
131
132 if (!mDvrConfigured) {
133 return Result::INVALID_STATE;
134 }
135
136 if (mType == DvrType::PLAYBACK) {
Amy Zhang9d5289f2021-02-18 13:56:45 -0800137 mDvrThreadRunning = true;
Amy Zhangbb94eeb2020-07-09 22:48:04 -0700138 pthread_create(&mDvrThread, NULL, __threadLoopPlayback, this);
139 pthread_setname_np(mDvrThread, "playback_waiting_loop");
140 } else if (mType == DvrType::RECORD) {
141 mRecordStatus = RecordStatus::DATA_READY;
142 mDemux->setIsRecording(mType == DvrType::RECORD);
143 }
144
145 // TODO start another thread to send filter status callback to the framework
146
147 return Result::SUCCESS;
148}
149
150Return<Result> Dvr::stop() {
151 ALOGV("%s", __FUNCTION__);
152
153 mDvrThreadRunning = false;
Amy Zhangbb94eeb2020-07-09 22:48:04 -0700154 lock_guard<mutex> lock(mDvrThreadLock);
155
156 mIsRecordStarted = false;
157 mDemux->setIsRecording(false);
158
159 return Result::SUCCESS;
160}
161
162Return<Result> Dvr::flush() {
163 ALOGV("%s", __FUNCTION__);
164
165 mRecordStatus = RecordStatus::DATA_READY;
166
167 return Result::SUCCESS;
168}
169
170Return<Result> Dvr::close() {
171 ALOGV("%s", __FUNCTION__);
172
Amy Zhang9d5289f2021-02-18 13:56:45 -0800173 mDvrThreadRunning = false;
174 lock_guard<mutex> lock(mDvrThreadLock);
Amy Zhangbb94eeb2020-07-09 22:48:04 -0700175 return Result::SUCCESS;
176}
177
178bool Dvr::createDvrMQ() {
179 ALOGV("%s", __FUNCTION__);
180
181 // Create a synchronized FMQ that supports blocking read/write
182 unique_ptr<DvrMQ> tmpDvrMQ = unique_ptr<DvrMQ>(new (nothrow) DvrMQ(mBufferSize, true));
183 if (!tmpDvrMQ->isValid()) {
184 ALOGW("[Dvr] Failed to create FMQ of DVR");
185 return false;
186 }
187
188 mDvrMQ = move(tmpDvrMQ);
189
190 if (EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrEventFlag) != OK) {
191 return false;
192 }
193
194 return true;
195}
196
197EventFlag* Dvr::getDvrEventFlag() {
198 return mDvrEventFlag;
199}
200
201void* Dvr::__threadLoopPlayback(void* user) {
202 Dvr* const self = static_cast<Dvr*>(user);
203 self->playbackThreadLoop();
204 return 0;
205}
206
207void Dvr::playbackThreadLoop() {
208 ALOGD("[Dvr] playback threadLoop start.");
209 lock_guard<mutex> lock(mDvrThreadLock);
Amy Zhangbb94eeb2020-07-09 22:48:04 -0700210
211 while (mDvrThreadRunning) {
212 uint32_t efState = 0;
213 status_t status =
214 mDvrEventFlag->wait(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY),
215 &efState, WAIT_TIMEOUT, true /* retry on spurious wake */);
216 if (status != OK) {
217 ALOGD("[Dvr] wait for data ready on the playback FMQ");
218 continue;
219 }
220
Amy Zhang160a72c2021-04-24 21:15:26 -0700221 // If the both dvr playback and dvr record are created, the playback will be treated as
222 // the source of the record. isVirtualFrontend set to true would direct the dvr playback
223 // input to the demux record filters or live broadcast filters.
224 bool isRecording = mDemux->isRecording();
225 bool isVirtualFrontend = isRecording;
226
Amy Zhangbb94eeb2020-07-09 22:48:04 -0700227 if (mDvrSettings.playback().dataFormat == DataFormat::ES) {
Amy Zhang160a72c2021-04-24 21:15:26 -0700228 if (!processEsDataOnPlayback(isVirtualFrontend, isRecording)) {
Amy Zhangbb94eeb2020-07-09 22:48:04 -0700229 ALOGE("[Dvr] playback es data failed to be filtered. Ending thread");
230 break;
231 }
232 maySendPlaybackStatusCallback();
Amy Zhang68afca62020-07-20 18:28:58 -0700233 continue;
Amy Zhangbb94eeb2020-07-09 22:48:04 -0700234 }
Amy Zhang160a72c2021-04-24 21:15:26 -0700235
Amy Zhangbb94eeb2020-07-09 22:48:04 -0700236 // Our current implementation filter the data and write it into the filter FMQ immediately
237 // after the DATA_READY from the VTS/framework
Amy Zhang68afca62020-07-20 18:28:58 -0700238 // This is for the non-ES data source, real playback use case handling.
Amy Zhang160a72c2021-04-24 21:15:26 -0700239 if (!readPlaybackFMQ(isVirtualFrontend, isRecording) ||
240 !startFilterDispatcher(isVirtualFrontend, isRecording)) {
Amy Zhangbb94eeb2020-07-09 22:48:04 -0700241 ALOGE("[Dvr] playback data failed to be filtered. Ending thread");
242 break;
243 }
244
245 maySendPlaybackStatusCallback();
246 }
247
248 mDvrThreadRunning = false;
249 ALOGD("[Dvr] playback thread ended.");
250}
251
252void Dvr::maySendPlaybackStatusCallback() {
253 lock_guard<mutex> lock(mPlaybackStatusLock);
254 int availableToRead = mDvrMQ->availableToRead();
255 int availableToWrite = mDvrMQ->availableToWrite();
256
257 PlaybackStatus newStatus = checkPlaybackStatusChange(availableToWrite, availableToRead,
258 mDvrSettings.playback().highThreshold,
259 mDvrSettings.playback().lowThreshold);
260 if (mPlaybackStatus != newStatus) {
261 mCallback->onPlaybackStatus(newStatus);
262 mPlaybackStatus = newStatus;
263 }
264}
265
266PlaybackStatus Dvr::checkPlaybackStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
267 uint32_t highThreshold, uint32_t lowThreshold) {
268 if (availableToWrite == 0) {
269 return PlaybackStatus::SPACE_FULL;
270 } else if (availableToRead > highThreshold) {
271 return PlaybackStatus::SPACE_ALMOST_FULL;
272 } else if (availableToRead < lowThreshold) {
273 return PlaybackStatus::SPACE_ALMOST_EMPTY;
274 } else if (availableToRead == 0) {
275 return PlaybackStatus::SPACE_EMPTY;
276 }
277 return mPlaybackStatus;
278}
279
280bool Dvr::readPlaybackFMQ(bool isVirtualFrontend, bool isRecording) {
281 // Read playback data from the input FMQ
282 int size = mDvrMQ->availableToRead();
283 int playbackPacketSize = mDvrSettings.playback().packetSize;
284 vector<uint8_t> dataOutputBuffer;
285 dataOutputBuffer.resize(playbackPacketSize);
286 // Dispatch the packet to the PID matching filter output buffer
287 for (int i = 0; i < size / playbackPacketSize; i++) {
288 if (!mDvrMQ->read(dataOutputBuffer.data(), playbackPacketSize)) {
289 return false;
290 }
291 if (isVirtualFrontend) {
292 if (isRecording) {
293 mDemux->sendFrontendInputToRecord(dataOutputBuffer);
294 } else {
295 mDemux->startBroadcastTsFilter(dataOutputBuffer);
296 }
297 } else {
298 startTpidFilter(dataOutputBuffer);
299 }
300 }
301
302 return true;
303}
304
305bool Dvr::processEsDataOnPlayback(bool isVirtualFrontend, bool isRecording) {
306 // Read ES from the DVR FMQ
307 // Note that currently we only provides ES with metaData in a specific format to be parsed.
308 // The ES size should be smaller than the Playback FMQ size to avoid reading truncated data.
309 int size = mDvrMQ->availableToRead();
310 vector<uint8_t> dataOutputBuffer;
311 dataOutputBuffer.resize(size);
312 if (!mDvrMQ->read(dataOutputBuffer.data(), size)) {
313 return false;
314 }
315
316 int metaDataSize = size;
317 int totalFrames = 0;
318 int videoEsDataSize = 0;
319 int audioEsDataSize = 0;
320 int audioPid = 0;
321 int videoPid = 0;
322
323 vector<MediaEsMetaData> esMeta;
324 int videoReadPointer = 0;
325 int audioReadPointer = 0;
326 int frameCount = 0;
327 // Get meta data from the es
328 for (int i = 0; i < metaDataSize; i++) {
329 switch (dataOutputBuffer[i]) {
330 case 'm':
331 metaDataSize = 0;
332 getMetaDataValue(i, dataOutputBuffer.data(), metaDataSize);
333 videoReadPointer = metaDataSize;
334 continue;
335 case 'l':
336 getMetaDataValue(i, dataOutputBuffer.data(), totalFrames);
337 esMeta.resize(totalFrames);
338 continue;
339 case 'V':
340 getMetaDataValue(i, dataOutputBuffer.data(), videoEsDataSize);
341 audioReadPointer = metaDataSize + videoEsDataSize;
342 continue;
343 case 'A':
344 getMetaDataValue(i, dataOutputBuffer.data(), audioEsDataSize);
345 continue;
346 case 'p':
347 if (dataOutputBuffer[++i] == 'a') {
348 getMetaDataValue(i, dataOutputBuffer.data(), audioPid);
349 } else if (dataOutputBuffer[i] == 'v') {
350 getMetaDataValue(i, dataOutputBuffer.data(), videoPid);
351 }
352 continue;
353 case 'v':
354 case 'a':
355 if (dataOutputBuffer[i + 1] != ',') {
356 ALOGE("[Dvr] Invalid format meta data.");
357 return false;
358 }
359 esMeta[frameCount] = {
360 .isAudio = dataOutputBuffer[i] == 'a' ? true : false,
361 };
362 i += 5; // Move to Len
363 getMetaDataValue(i, dataOutputBuffer.data(), esMeta[frameCount].len);
364 if (esMeta[frameCount].isAudio) {
365 esMeta[frameCount].startIndex = audioReadPointer;
366 audioReadPointer += esMeta[frameCount].len;
367 } else {
368 esMeta[frameCount].startIndex = videoReadPointer;
369 videoReadPointer += esMeta[frameCount].len;
370 }
371 i += 4; // move to PTS
372 getMetaDataValue(i, dataOutputBuffer.data(), esMeta[frameCount].pts);
373 frameCount++;
374 continue;
375 default:
376 continue;
377 }
378 }
379
380 if (frameCount != totalFrames) {
381 ALOGE("[Dvr] Invalid meta data, frameCount=%d, totalFrames reported=%d", frameCount,
382 totalFrames);
383 return false;
384 }
385
386 if (metaDataSize + audioEsDataSize + videoEsDataSize != size) {
387 ALOGE("[Dvr] Invalid meta data, metaSize=%d, videoSize=%d, audioSize=%d, totolSize=%d",
388 metaDataSize, videoEsDataSize, audioEsDataSize, size);
389 return false;
390 }
391
392 // Read es raw data from the FMQ per meta data built previously
393 vector<uint8_t> frameData;
394 map<uint64_t, sp<IFilter>>::iterator it;
395 int pid = 0;
396 for (int i = 0; i < totalFrames; i++) {
397 frameData.resize(esMeta[i].len);
398 pid = esMeta[i].isAudio ? audioPid : videoPid;
Amy Zhang68afca62020-07-20 18:28:58 -0700399 memcpy(frameData.data(), dataOutputBuffer.data() + esMeta[i].startIndex, esMeta[i].len);
400 // Send to the media filters or record filters
401 if (!isRecording) {
Amy Zhangbb94eeb2020-07-09 22:48:04 -0700402 for (it = mFilters.begin(); it != mFilters.end(); it++) {
403 if (pid == mDemux->getFilterTpid(it->first)) {
404 mDemux->updateMediaFilterOutput(it->first, frameData,
405 static_cast<uint64_t>(esMeta[i].pts));
Amy Zhangbb94eeb2020-07-09 22:48:04 -0700406 }
407 }
Amy Zhang68afca62020-07-20 18:28:58 -0700408 } else {
409 mDemux->sendFrontendInputToRecord(frameData, pid, static_cast<uint64_t>(esMeta[i].pts));
Amy Zhangbb94eeb2020-07-09 22:48:04 -0700410 }
Amy Zhang68afca62020-07-20 18:28:58 -0700411 startFilterDispatcher(isVirtualFrontend, isRecording);
Amy Zhang990ee762020-07-30 17:24:37 -0700412 frameData.clear();
Amy Zhangbb94eeb2020-07-09 22:48:04 -0700413 }
414
415 return true;
416}
417
418void Dvr::getMetaDataValue(int& index, uint8_t* dataOutputBuffer, int& value) {
419 index += 2; // Move the pointer across the ":" to the value
420 while (dataOutputBuffer[index] != ',' && dataOutputBuffer[index] != '\n') {
421 value = ((dataOutputBuffer[index++] - 48) + value * 10);
422 }
423}
424
425void Dvr::startTpidFilter(vector<uint8_t> data) {
426 map<uint64_t, sp<IFilter>>::iterator it;
427 for (it = mFilters.begin(); it != mFilters.end(); it++) {
428 uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff));
429 if (DEBUG_DVR) {
430 ALOGW("[Dvr] start ts filter pid: %d", pid);
431 }
432 if (pid == mDemux->getFilterTpid(it->first)) {
433 mDemux->updateFilterOutput(it->first, data);
434 }
435 }
436}
437
438bool Dvr::startFilterDispatcher(bool isVirtualFrontend, bool isRecording) {
439 if (isVirtualFrontend) {
440 if (isRecording) {
441 return mDemux->startRecordFilterDispatcher();
442 } else {
443 return mDemux->startBroadcastFilterDispatcher();
444 }
445 }
446
447 map<uint64_t, sp<IFilter>>::iterator it;
448 // Handle the output data per filter type
449 for (it = mFilters.begin(); it != mFilters.end(); it++) {
450 if (mDemux->startFilterHandler(it->first) != Result::SUCCESS) {
451 return false;
452 }
453 }
454
455 return true;
456}
457
458bool Dvr::writeRecordFMQ(const vector<uint8_t>& data) {
459 lock_guard<mutex> lock(mWriteLock);
460 if (mRecordStatus == RecordStatus::OVERFLOW) {
461 ALOGW("[Dvr] stops writing and wait for the client side flushing.");
462 return true;
463 }
464 if (mDvrMQ->write(data.data(), data.size())) {
465 mDvrEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
466 maySendRecordStatusCallback();
467 return true;
468 }
469
470 maySendRecordStatusCallback();
471 return false;
472}
473
474void Dvr::maySendRecordStatusCallback() {
475 lock_guard<mutex> lock(mRecordStatusLock);
476 int availableToRead = mDvrMQ->availableToRead();
477 int availableToWrite = mDvrMQ->availableToWrite();
478
479 RecordStatus newStatus = checkRecordStatusChange(availableToWrite, availableToRead,
480 mDvrSettings.record().highThreshold,
481 mDvrSettings.record().lowThreshold);
482 if (mRecordStatus != newStatus) {
483 mCallback->onRecordStatus(newStatus);
484 mRecordStatus = newStatus;
485 }
486}
487
488RecordStatus Dvr::checkRecordStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
489 uint32_t highThreshold, uint32_t lowThreshold) {
490 if (availableToWrite == 0) {
491 return DemuxFilterStatus::OVERFLOW;
492 } else if (availableToRead > highThreshold) {
493 return DemuxFilterStatus::HIGH_WATER;
494 } else if (availableToRead < lowThreshold) {
495 return DemuxFilterStatus::LOW_WATER;
496 }
497 return mRecordStatus;
498}
499
500bool Dvr::addPlaybackFilter(uint64_t filterId, sp<IFilter> filter) {
501 mFilters[filterId] = filter;
502 return true;
503}
504
505bool Dvr::removePlaybackFilter(uint64_t filterId) {
506 mFilters.erase(filterId);
507 return true;
508}
509} // namespace implementation
510} // namespace V1_0
511} // namespace tuner
512} // namespace tv
513} // namespace hardware
514} // namespace android