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