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