blob: c591d079a86997b9939dd259fc39effe4ddf1688 [file] [log] [blame]
Hongguang4092f2f2021-07-08 18:49:12 -07001/*
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>
Hongguange423acd2021-07-27 16:56:47 -070021#include <aidl/android/hardware/tv/tuner/Result.h>
Hongguang4092f2f2021-07-08 18:49:12 -070022
23#include <utils/Log.h>
24#include "Dvr.h"
25
26namespace aidl {
27namespace android {
28namespace hardware {
29namespace tv {
30namespace tuner {
31
32#define WAIT_TIMEOUT 3000000000
33
Hongguang Chenff2c6b02021-08-07 00:12:26 +000034Dvr::Dvr(DvrType type, uint32_t bufferSize, const std::shared_ptr<IDvrCallback>& cb,
35 std::shared_ptr<Demux> demux) {
Hongguang4092f2f2021-07-08 18:49:12 -070036 mType = type;
37 mBufferSize = bufferSize;
38 mCallback = cb;
39 mDemux = demux;
40}
41
42Dvr::~Dvr() {
Hongguange423acd2021-07-27 16:56:47 -070043 // make sure thread has joined
44 close();
Hongguang4092f2f2021-07-08 18:49:12 -070045}
46
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
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
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)) {
Hongguange423acd2021-07-27 16:56:47 -070074 return ::ndk::ScopedAStatus::fromServiceSpecificError(
75 static_cast<int32_t>(Result::INVALID_ARGUMENT));
Hongguang4092f2f2021-07-08 18:49:12 -070076 }
77
78 return ::ndk::ScopedAStatus::ok();
79}
80
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)) {
Hongguange423acd2021-07-27 16:56:47 -070091 return ::ndk::ScopedAStatus::fromServiceSpecificError(
92 static_cast<int32_t>(Result::INVALID_ARGUMENT));
Hongguang4092f2f2021-07-08 18:49:12 -070093 }
94
95 return ::ndk::ScopedAStatus::ok();
96}
97
98::ndk::ScopedAStatus Dvr::start() {
99 ALOGV("%s", __FUNCTION__);
100 if (mDvrThreadRunning) {
101 return ::ndk::ScopedAStatus::ok();
102 }
103
104 if (!mCallback) {
Hongguange423acd2021-07-27 16:56:47 -0700105 return ::ndk::ScopedAStatus::fromServiceSpecificError(
106 static_cast<int32_t>(Result::NOT_INITIALIZED));
Hongguang4092f2f2021-07-08 18:49:12 -0700107 }
108
109 if (!mDvrConfigured) {
Hongguange423acd2021-07-27 16:56:47 -0700110 return ::ndk::ScopedAStatus::fromServiceSpecificError(
111 static_cast<int32_t>(Result::INVALID_STATE));
Hongguang4092f2f2021-07-08 18:49:12 -0700112 }
113
114 if (mType == DvrType::PLAYBACK) {
115 mDvrThreadRunning = true;
Hongguange423acd2021-07-27 16:56:47 -0700116 mDvrThread = std::thread(&Dvr::playbackThreadLoop, this);
Hongguang4092f2f2021-07-08 18:49:12 -0700117 } 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
127::ndk::ScopedAStatus Dvr::stop() {
128 ALOGV("%s", __FUNCTION__);
129
130 mDvrThreadRunning = false;
Hongguange423acd2021-07-27 16:56:47 -0700131 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.
Hongguang4092f2f2021-07-08 18:49:12 -0700136 mDemux->setIsRecording(false);
137
138 return ::ndk::ScopedAStatus::ok();
139}
140
141::ndk::ScopedAStatus Dvr::flush() {
142 ALOGV("%s", __FUNCTION__);
143
144 mRecordStatus = RecordStatus::DATA_READY;
145
146 return ::ndk::ScopedAStatus::ok();
147}
148
149::ndk::ScopedAStatus Dvr::close() {
150 ALOGV("%s", __FUNCTION__);
151
Hongguange423acd2021-07-27 16:56:47 -0700152 stop();
153
Hongguang4092f2f2021-07-08 18:49:12 -0700154 return ::ndk::ScopedAStatus::ok();
155}
156
157bool 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
176EventFlag* Dvr::getDvrEventFlag() {
177 return mDvrEventFlag;
178}
179
Hongguang2ecfc392021-11-23 10:29:15 -0800180binder_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
Hongguang4092f2f2021-07-08 18:49:12 -0700187void Dvr::playbackThreadLoop() {
188 ALOGD("[Dvr] playback threadLoop start.");
Hongguang4092f2f2021-07-08 18:49:12 -0700189
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
231void 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
246PlaybackStatus Dvr::checkPlaybackStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
Hongguang11da2cb2021-08-05 19:05:12 -0700247 int64_t highThreshold, int64_t lowThreshold) {
Hongguang4092f2f2021-07-08 18:49:12 -0700248 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
260bool Dvr::readPlaybackFMQ(bool isVirtualFrontend, bool isRecording) {
261 // Read playback data from the input FMQ
Hongguang11da2cb2021-08-05 19:05:12 -0700262 size_t size = mDvrMQ->availableToRead();
263 int64_t playbackPacketSize = mDvrSettings.get<DvrSettings::Tag::playback>().packetSize;
Hongguang4092f2f2021-07-08 18:49:12 -0700264 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
285bool 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
398void 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
405void 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
418bool 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
438bool 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
454void 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
469RecordStatus Dvr::checkRecordStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
Hongguang11da2cb2021-08-05 19:05:12 -0700470 int64_t highThreshold, int64_t lowThreshold) {
Hongguang4092f2f2021-07-08 18:49:12 -0700471 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
481bool Dvr::addPlaybackFilter(int64_t filterId, std::shared_ptr<IFilter> filter) {
482 mFilters[filterId] = filter;
483 return true;
484}
485
486bool 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