Refactor Tuner HAL Default Impl for Filter and Dvr separation
Test: manual
Bug: 135709325
Change-Id: I130f555315683fa02272f40d1e6209c5695c884a
diff --git a/tv/tuner/1.0/default/Android.bp b/tv/tuner/1.0/default/Android.bp
index 0ae8bcd..989e25c 100644
--- a/tv/tuner/1.0/default/Android.bp
+++ b/tv/tuner/1.0/default/Android.bp
@@ -4,9 +4,12 @@
vendor: true,
relative_install_path: "hw",
srcs: [
+ "Filter.cpp",
"Frontend.cpp",
"Descrambler.cpp",
"Demux.cpp",
+ "Dvr.cpp",
+ "TimeFilter.cpp",
"Tuner.cpp",
"Lnb.cpp",
"service.cpp",
diff --git a/tv/tuner/1.0/default/Demux.cpp b/tv/tuner/1.0/default/Demux.cpp
index 8bb79f9..c5921f7 100644
--- a/tv/tuner/1.0/default/Demux.cpp
+++ b/tv/tuner/1.0/default/Demux.cpp
@@ -28,45 +28,6 @@
#define WAIT_TIMEOUT 3000000000
-const std::vector<uint8_t> fakeDataInputBuffer{
- 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x1e, 0xdb,
- 0x01, 0x40, 0x16, 0xec, 0x04, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x0f, 0x03,
- 0xc5, 0x8b, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x68, 0xca, 0x8c, 0xb2, 0x00, 0x00, 0x01, 0x06,
- 0x05, 0xff, 0xff, 0x70, 0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8,
- 0x20, 0xd9, 0x23, 0xee, 0xef, 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63, 0x6f, 0x72,
- 0x65, 0x20, 0x31, 0x34, 0x32, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32, 0x36, 0x34, 0x2f, 0x4d,
- 0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, 0x43, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x63,
- 0x20, 0x2d, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30,
- 0x33, 0x2d, 0x32, 0x30, 0x31, 0x34, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
- 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c, 0x61, 0x6e, 0x2e, 0x6f,
- 0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20, 0x2d, 0x20,
- 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x20, 0x63, 0x61, 0x62, 0x61, 0x63, 0x3d,
- 0x30, 0x20, 0x72, 0x65, 0x66, 0x3d, 0x32, 0x20, 0x64, 0x65, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
- 0x3d, 0x31, 0x3a, 0x30, 0x3a, 0x30, 0x20, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x65, 0x3d,
- 0x30, 0x78, 0x31, 0x3a, 0x30, 0x78, 0x31, 0x31, 0x31, 0x20, 0x6d, 0x65, 0x3d, 0x68, 0x65,
- 0x78, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x65, 0x3d, 0x37, 0x20, 0x70, 0x73, 0x79, 0x3d, 0x31,
- 0x20, 0x70, 0x73, 0x79, 0x5f, 0x72, 0x64, 0x3d, 0x31, 0x2e, 0x30, 0x30, 0x3a, 0x30, 0x2e,
- 0x30, 0x30, 0x20, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x3d, 0x31, 0x20,
- 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x3d, 0x31, 0x36, 0x20, 0x63, 0x68, 0x72,
- 0x6f, 0x6d, 0x61, 0x5f, 0x6d, 0x65, 0x3d, 0x31, 0x20, 0x74, 0x72, 0x65, 0x6c, 0x6c, 0x69,
- 0x73, 0x3d, 0x31, 0x20, 0x38, 0x78, 0x38, 0x64, 0x63, 0x74, 0x3d, 0x30, 0x20, 0x63, 0x71,
- 0x6d, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x61, 0x64, 0x7a, 0x6f, 0x6e, 0x65, 0x3d, 0x32, 0x31,
- 0x2c, 0x31, 0x31, 0x20, 0x66, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x73, 0x6b, 0x69, 0x70, 0x3d,
- 0x31, 0x20, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x5f, 0x71, 0x70, 0x5f, 0x6f, 0x66, 0x66,
- 0x73, 0x65, 0x74, 0x3d, 0x2d, 0x32, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d,
- 0x36, 0x30, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x61, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x68,
- 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x35, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x64, 0x5f,
- 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x30, 0x20, 0x6e, 0x72, 0x3d, 0x30, 0x20,
- 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x20, 0x69, 0x6e, 0x74, 0x65,
- 0x72, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x3d, 0x30, 0x20, 0x62, 0x6c, 0x75, 0x72, 0x61, 0x79,
- 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x3d, 0x30, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74,
- 0x72, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x3d, 0x30, 0x20,
- 0x62, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x3d, 0x30, 0x20, 0x77, 0x65, 0x69, 0x67, 0x68,
- 0x74, 0x70, 0x3d, 0x30, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x3d, 0x32, 0x35, 0x30,
- 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x3d, 0x32, 0x35, 0x20,
- 0x73, 0x63, 0x65, 0x6e, 0x65,
-};
-
Demux::Demux(uint32_t demuxId, sp<Tuner> tuner) {
mDemuxId = demuxId;
mTunerService = tuner;
@@ -93,8 +54,8 @@
return startBroadcastInputLoop();
}
-Return<void> Demux::addFilter(DemuxFilterType type, uint32_t bufferSize,
- const sp<IDemuxCallback>& cb, addFilter_cb _hidl_cb) {
+Return<void> Demux::openFilter(const DemuxFilterType& type, uint32_t bufferSize,
+ const sp<IFilterCallback>& cb, openFilter_cb _hidl_cb) {
ALOGV("%s", __FUNCTION__);
uint32_t filterId;
@@ -105,137 +66,39 @@
mUnusedFilterIds.erase(filterId);
} else {
filterId = ++mLastUsedFilterId;
-
- mFilterCallbacks.resize(filterId + 1);
- mFilterMQs.resize(filterId + 1);
- mFilterEvents.resize(filterId + 1);
- mFilterEventFlags.resize(filterId + 1);
- mFilterThreadRunning.resize(filterId + 1);
- mFilterThreads.resize(filterId + 1);
- mFilterPids.resize(filterId + 1);
- mFilterOutputs.resize(filterId + 1);
- mFilterStatus.resize(filterId + 1);
}
mUsedFilterIds.insert(filterId);
- if ((type != DemuxFilterType::PCR || type != DemuxFilterType::TS) && cb == nullptr) {
+ if (cb == nullptr) {
ALOGW("callback can't be null");
- _hidl_cb(Result::INVALID_ARGUMENT, filterId);
+ _hidl_cb(Result::INVALID_ARGUMENT, new Filter());
return Void();
}
- // Add callback
- mFilterCallbacks[filterId] = cb;
+ sp<Filter> filter = new Filter(type, filterId, bufferSize, cb, this);
- // Mapping from the filter ID to the filter event
- DemuxFilterEvent event{
- .filterId = filterId,
- .filterType = type,
- };
- mFilterEvents[filterId] = event;
-
- if (!createFilterMQ(bufferSize, filterId)) {
- _hidl_cb(Result::UNKNOWN_ERROR, -1);
+ if (!filter->createFilterMQ()) {
+ _hidl_cb(Result::UNKNOWN_ERROR, filter);
return Void();
}
- _hidl_cb(Result::SUCCESS, filterId);
+ mFilters[filterId] = filter;
+
+ _hidl_cb(Result::SUCCESS, filter);
return Void();
}
-Return<void> Demux::getFilterQueueDesc(uint32_t filterId, getFilterQueueDesc_cb _hidl_cb) {
+Return<void> Demux::openTimeFilter(openTimeFilter_cb _hidl_cb) {
ALOGV("%s", __FUNCTION__);
- if (mUsedFilterIds.find(filterId) == mUsedFilterIds.end()) {
- ALOGW("No filter with id: %d exists to get desc", filterId);
- _hidl_cb(Result::INVALID_ARGUMENT, FilterMQ::Descriptor());
- return Void();
- }
+ sp<TimeFilter> timeFilter = new TimeFilter(this);
- _hidl_cb(Result::SUCCESS, *mFilterMQs[filterId]->getDesc());
+ _hidl_cb(Result::SUCCESS, timeFilter);
return Void();
}
-Return<Result> Demux::configureFilter(uint32_t filterId, const DemuxFilterSettings& settings) {
- ALOGV("%s", __FUNCTION__);
-
- switch (mFilterEvents[filterId].filterType) {
- case DemuxFilterType::SECTION:
- mFilterPids[filterId] = settings.section().tpid;
- break;
- case DemuxFilterType::PES:
- mFilterPids[filterId] = settings.pesData().tpid;
- break;
- case DemuxFilterType::TS:
- mFilterPids[filterId] = settings.ts().tpid;
- break;
- case DemuxFilterType::AUDIO:
- mFilterPids[filterId] = settings.audio().tpid;
- break;
- case DemuxFilterType::VIDEO:
- mFilterPids[filterId] = settings.video().tpid;
- break;
- case DemuxFilterType::RECORD:
- mFilterPids[filterId] = settings.record().tpid;
- break;
- case DemuxFilterType::PCR:
- mFilterPids[filterId] = settings.pcr().tpid;
- break;
- default:
- return Result::UNKNOWN_ERROR;
- }
- return Result::SUCCESS;
-}
-
-Return<Result> Demux::startFilter(uint32_t filterId) {
- ALOGV("%s", __FUNCTION__);
- Result result;
-
- if (mUsedFilterIds.find(filterId) == mUsedFilterIds.end()) {
- ALOGW("No filter with id: %d exists to start filter", filterId);
- return Result::INVALID_ARGUMENT;
- }
-
- result = startFilterLoop(filterId);
-
- return result;
-}
-
-Return<Result> Demux::stopFilter(uint32_t filterId) {
- ALOGV("%s", __FUNCTION__);
-
- mFilterThreadRunning[filterId] = false;
-
- std::lock_guard<std::mutex> lock(mFilterThreadLock);
-
- return Result::SUCCESS;
-}
-
-Return<Result> Demux::flushFilter(uint32_t filterId) {
- ALOGV("%s", __FUNCTION__);
-
- // temp implementation to flush the FMQ
- int size = mFilterMQs[filterId]->availableToRead();
- char* buffer = new char[size];
- mOutputMQ->read((unsigned char*)&buffer[0], size);
- delete[] buffer;
- mFilterStatus[filterId] = DemuxFilterStatus::DATA_READY;
-
- return Result::SUCCESS;
-}
-
-Return<Result> Demux::removeFilter(uint32_t filterId) {
- ALOGV("%s", __FUNCTION__);
-
- // resetFilterRecords(filterId);
- mUsedFilterIds.erase(filterId);
- mUnusedFilterIds.insert(filterId);
-
- return Result::SUCCESS;
-}
-
-Return<void> Demux::getAvSyncHwId(uint32_t /* filterId */, getAvSyncHwId_cb _hidl_cb) {
+Return<void> Demux::getAvSyncHwId(const sp<IFilter>& /* filter */, getAvSyncHwId_cb _hidl_cb) {
ALOGV("%s", __FUNCTION__);
AvSyncHwId avSyncHwId = 0;
@@ -256,588 +119,81 @@
Return<Result> Demux::close() {
ALOGV("%s", __FUNCTION__);
- set<uint32_t>::iterator it;
- mInputThread = 0;
- mOutputThread = 0;
- mFilterThreads.clear();
mUnusedFilterIds.clear();
mUsedFilterIds.clear();
- mFilterCallbacks.clear();
- mFilterMQs.clear();
- mFilterEvents.clear();
- mFilterEventFlags.clear();
- mFilterOutputs.clear();
- mFilterPids.clear();
mLastUsedFilterId = -1;
return Result::SUCCESS;
}
-Return<Result> Demux::addOutput(uint32_t bufferSize, const sp<IDemuxCallback>& cb) {
+Return<void> Demux::openDvr(DvrType type, uint32_t bufferSize, const sp<IDvrCallback>& cb,
+ openDvr_cb _hidl_cb) {
ALOGV("%s", __FUNCTION__);
- // Create a synchronized FMQ that supports blocking read/write
- std::unique_ptr<FilterMQ> tmpFilterMQ =
- std::unique_ptr<FilterMQ>(new (std::nothrow) FilterMQ(bufferSize, true));
- if (!tmpFilterMQ->isValid()) {
- ALOGW("Failed to create output FMQ");
- return Result::UNKNOWN_ERROR;
- }
-
- mOutputMQ = std::move(tmpFilterMQ);
-
- if (EventFlag::createEventFlag(mOutputMQ->getEventFlagWord(), &mOutputEventFlag) != OK) {
- return Result::UNKNOWN_ERROR;
- }
-
- mOutputCallback = cb;
-
- return Result::SUCCESS;
-}
-
-Return<void> Demux::getOutputQueueDesc(getOutputQueueDesc_cb _hidl_cb) {
- ALOGV("%s", __FUNCTION__);
-
- if (!mOutputMQ) {
- _hidl_cb(Result::NOT_INITIALIZED, FilterMQ::Descriptor());
+ if (cb == nullptr) {
+ ALOGW("DVR callback can't be null");
+ _hidl_cb(Result::INVALID_ARGUMENT, new Dvr());
return Void();
}
- _hidl_cb(Result::SUCCESS, *mOutputMQ->getDesc());
- return Void();
-}
+ sp<Dvr> dvr = new Dvr(type, bufferSize, cb, this);
-Return<Result> Demux::configureOutput(const DemuxOutputSettings& settings) {
- ALOGV("%s", __FUNCTION__);
-
- mOutputConfigured = true;
- mOutputSettings = settings;
- return Result::SUCCESS;
-}
-
-Return<Result> Demux::attachOutputFilter(uint32_t /*filterId*/) {
- ALOGV("%s", __FUNCTION__);
-
- return Result::SUCCESS;
-}
-
-Return<Result> Demux::detachOutputFilter(uint32_t /* filterId */) {
- ALOGV("%s", __FUNCTION__);
-
- return Result::SUCCESS;
-}
-
-Return<Result> Demux::startOutput() {
- ALOGV("%s", __FUNCTION__);
-
- return Result::SUCCESS;
-}
-
-Return<Result> Demux::stopOutput() {
- ALOGV("%s", __FUNCTION__);
-
- return Result::SUCCESS;
-}
-
-Return<Result> Demux::flushOutput() {
- ALOGV("%s", __FUNCTION__);
-
- return Result::SUCCESS;
-}
-
-Return<Result> Demux::removeOutput() {
- ALOGV("%s", __FUNCTION__);
-
- return Result::SUCCESS;
-}
-
-Return<Result> Demux::addInput(uint32_t bufferSize, const sp<IDemuxCallback>& cb) {
- ALOGV("%s", __FUNCTION__);
-
- // Create a synchronized FMQ that supports blocking read/write
- std::unique_ptr<FilterMQ> tmpInputMQ =
- std::unique_ptr<FilterMQ>(new (std::nothrow) FilterMQ(bufferSize, true));
- if (!tmpInputMQ->isValid()) {
- ALOGW("Failed to create input FMQ");
- return Result::UNKNOWN_ERROR;
- }
-
- mInputMQ = std::move(tmpInputMQ);
-
- if (EventFlag::createEventFlag(mInputMQ->getEventFlagWord(), &mInputEventFlag) != OK) {
- return Result::UNKNOWN_ERROR;
- }
-
- mInputCallback = cb;
-
- return Result::SUCCESS;
-}
-
-Return<void> Demux::getInputQueueDesc(getInputQueueDesc_cb _hidl_cb) {
- ALOGV("%s", __FUNCTION__);
-
- if (!mInputMQ) {
- _hidl_cb(Result::NOT_INITIALIZED, FilterMQ::Descriptor());
+ if (!dvr->createDvrMQ()) {
+ _hidl_cb(Result::UNKNOWN_ERROR, dvr);
return Void();
}
- _hidl_cb(Result::SUCCESS, *mInputMQ->getDesc());
+ _hidl_cb(Result::SUCCESS, dvr);
return Void();
}
-Return<Result> Demux::configureInput(const DemuxInputSettings& settings) {
+Result Demux::removeFilter(uint32_t filterId) {
ALOGV("%s", __FUNCTION__);
- mInputConfigured = true;
- mInputSettings = settings;
+ // resetFilterRecords(filterId);
+ mUsedFilterIds.erase(filterId);
+ mUnusedFilterIds.insert(filterId);
+ mFilters.erase(filterId);
return Result::SUCCESS;
}
-Return<Result> Demux::startInput() {
- ALOGV("%s", __FUNCTION__);
-
- if (!mInputCallback) {
- return Result::NOT_INITIALIZED;
- }
-
- if (!mInputConfigured) {
- return Result::INVALID_STATE;
- }
-
- pthread_create(&mInputThread, NULL, __threadLoopInput, this);
- pthread_setname_np(mInputThread, "demux_input_waiting_loop");
-
- // TODO start another thread to send filter status callback to the framework
-
- return Result::SUCCESS;
-}
-
-Return<Result> Demux::stopInput() {
- ALOGV("%s", __FUNCTION__);
-
- mInputThreadRunning = false;
-
- std::lock_guard<std::mutex> lock(mInputThreadLock);
-
- return Result::SUCCESS;
-}
-
-Return<Result> Demux::flushInput() {
- ALOGV("%s", __FUNCTION__);
-
- return Result::SUCCESS;
-}
-
-Return<Result> Demux::removeInput() {
- ALOGV("%s", __FUNCTION__);
-
- mInputMQ = nullptr;
-
- return Result::SUCCESS;
-}
-
-Result Demux::startFilterLoop(uint32_t filterId) {
- struct ThreadArgs* threadArgs = (struct ThreadArgs*)malloc(sizeof(struct ThreadArgs));
- threadArgs->user = this;
- threadArgs->filterId = filterId;
-
- pthread_t mFilterThread;
- pthread_create(&mFilterThread, NULL, __threadLoopFilter, (void*)threadArgs);
- mFilterThreads[filterId] = mFilterThread;
- pthread_setname_np(mFilterThread, "demux_filter_waiting_loop");
-
- return Result::SUCCESS;
-}
-
-Result Demux::startSectionFilterHandler(uint32_t filterId) {
- if (mFilterOutputs[filterId].empty()) {
- return Result::SUCCESS;
- }
- if (!writeSectionsAndCreateEvent(filterId, mFilterOutputs[filterId])) {
- ALOGD("[Demux] filter %d fails to write into FMQ. Ending thread", filterId);
- return Result::UNKNOWN_ERROR;
- }
-
- mFilterOutputs[filterId].clear();
-
- return Result::SUCCESS;
-}
-
-Result Demux::startPesFilterHandler(uint32_t filterId) {
- std::lock_guard<std::mutex> lock(mFilterEventLock);
- if (mFilterOutputs[filterId].empty()) {
- return Result::SUCCESS;
- }
-
- for (int i = 0; i < mFilterOutputs[filterId].size(); i += 188) {
- if (mPesSizeLeft == 0) {
- uint32_t prefix = (mFilterOutputs[filterId][i + 4] << 16) |
- (mFilterOutputs[filterId][i + 5] << 8) |
- mFilterOutputs[filterId][i + 6];
- ALOGD("[Demux] prefix %d", prefix);
- if (prefix == 0x000001) {
- // TODO handle mulptiple Pes filters
- mPesSizeLeft =
- (mFilterOutputs[filterId][i + 8] << 8) | mFilterOutputs[filterId][i + 9];
- mPesSizeLeft += 6;
- ALOGD("[Demux] pes data length %d", mPesSizeLeft);
- } else {
- continue;
- }
- }
-
- int endPoint = min(184, mPesSizeLeft);
- // append data and check size
- vector<uint8_t>::const_iterator first = mFilterOutputs[filterId].begin() + i + 4;
- vector<uint8_t>::const_iterator last = mFilterOutputs[filterId].begin() + i + 4 + endPoint;
- mPesOutput.insert(mPesOutput.end(), first, last);
- // size does not match then continue
- mPesSizeLeft -= endPoint;
- if (mPesSizeLeft > 0) {
- continue;
- }
- // size match then create event
- if (!writeDataToFilterMQ(mPesOutput, filterId)) {
- mFilterOutputs[filterId].clear();
- return Result::INVALID_STATE;
- }
- maySendFilterStatusCallback(filterId);
- DemuxFilterPesEvent pesEvent;
- pesEvent = {
- // temp dump meta data
- .streamId = mPesOutput[3],
- .dataLength = static_cast<uint16_t>(mPesOutput.size()),
- };
- ALOGD("[Demux] assembled pes data length %d", pesEvent.dataLength);
-
- int size = mFilterEvents[filterId].events.size();
- mFilterEvents[filterId].events.resize(size + 1);
- mFilterEvents[filterId].events[size].pes(pesEvent);
- mPesOutput.clear();
- }
-
- mFilterOutputs[filterId].clear();
-
- return Result::SUCCESS;
-}
-
-Result Demux::startTsFilterHandler() {
- // TODO handle starting TS filter
- return Result::SUCCESS;
-}
-
-Result Demux::startMediaFilterHandler(uint32_t filterId) {
- DemuxFilterMediaEvent mediaEvent;
- mediaEvent = {
- // temp dump meta data
- .pts = 0,
- .dataLength = 530,
- .secureMemory = nullptr,
- };
- mFilterEvents[filterId].events.resize(1);
- mFilterEvents[filterId].events[0].media() = mediaEvent;
-
- mFilterOutputs[filterId].clear();
- // TODO handle write FQM for media stream
- return Result::SUCCESS;
-}
-
-Result Demux::startRecordFilterHandler(uint32_t filterId) {
- DemuxFilterRecordEvent recordEvent;
- recordEvent = {
- // temp dump meta data
- .tpid = 0,
- .packetNum = 0,
- };
- recordEvent.indexMask.tsIndexMask() = 0x01;
- mFilterEvents[filterId].events.resize(1);
- mFilterEvents[filterId].events[0].ts() = recordEvent;
-
- mFilterOutputs[filterId].clear();
- return Result::SUCCESS;
-}
-
-Result Demux::startPcrFilterHandler() {
- // TODO handle starting PCR filter
- return Result::SUCCESS;
-}
-
-bool Demux::createFilterMQ(uint32_t bufferSize, uint32_t filterId) {
- ALOGV("%s", __FUNCTION__);
-
- // Create a synchronized FMQ that supports blocking read/write
- std::unique_ptr<FilterMQ> tmpFilterMQ =
- std::unique_ptr<FilterMQ>(new (std::nothrow) FilterMQ(bufferSize, true));
- if (!tmpFilterMQ->isValid()) {
- ALOGW("Failed to create FMQ of filter with id: %d", filterId);
- return false;
- }
-
- mFilterMQs[filterId] = std::move(tmpFilterMQ);
-
- EventFlag* filterEventFlag;
- if (EventFlag::createEventFlag(mFilterMQs[filterId]->getEventFlagWord(), &filterEventFlag) !=
- OK) {
- return false;
- }
- mFilterEventFlags[filterId] = filterEventFlag;
-
- return true;
-}
-
-bool Demux::writeSectionsAndCreateEvent(uint32_t filterId, vector<uint8_t> data) {
- // TODO check how many sections has been read
- std::lock_guard<std::mutex> lock(mFilterEventLock);
- if (!writeDataToFilterMQ(data, filterId)) {
- return false;
- }
- int size = mFilterEvents[filterId].events.size();
- mFilterEvents[filterId].events.resize(size + 1);
- DemuxFilterSectionEvent secEvent;
- secEvent = {
- // temp dump meta data
- .tableId = 0,
- .version = 1,
- .sectionNum = 1,
- .dataLength = static_cast<uint16_t>(data.size()),
- };
- mFilterEvents[filterId].events[size].section(secEvent);
- return true;
-}
-
-bool Demux::writeDataToFilterMQ(const std::vector<uint8_t>& data, uint32_t filterId) {
- std::lock_guard<std::mutex> lock(mWriteLock);
- if (mFilterMQs[filterId]->write(data.data(), data.size())) {
- return true;
- }
- return false;
-}
-
-bool Demux::readInputFMQ() {
- // Read input data from the input FMQ
- int size = mInputMQ->availableToRead();
- int inputPacketSize = mInputSettings.packetSize;
- vector<uint8_t> dataOutputBuffer;
- dataOutputBuffer.resize(inputPacketSize);
-
- // Dispatch the packet to the PID matching filter output buffer
- for (int i = 0; i < size / inputPacketSize; i++) {
- if (!mInputMQ->read(dataOutputBuffer.data(), inputPacketSize)) {
- return false;
- }
- startTsFilter(dataOutputBuffer);
- }
-
- return true;
-}
-
void Demux::startTsFilter(vector<uint8_t> data) {
set<uint32_t>::iterator it;
for (it = mUsedFilterIds.begin(); it != mUsedFilterIds.end(); it++) {
uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff));
- ALOGW("start ts filter pid: %d", pid);
- if (pid == mFilterPids[*it]) {
- mFilterOutputs[*it].insert(mFilterOutputs[*it].end(), data.begin(), data.end());
+ if (DEBUG_FILTER) {
+ ALOGW("start ts filter pid: %d", pid);
+ }
+ if (pid == mFilters[*it]->getTpid()) {
+ mFilters[*it]->updateFilterOutput(data);
}
}
}
bool Demux::startFilterDispatcher() {
- Result result;
set<uint32_t>::iterator it;
// Handle the output data per filter type
for (it = mUsedFilterIds.begin(); it != mUsedFilterIds.end(); it++) {
- switch (mFilterEvents[*it].filterType) {
- case DemuxFilterType::SECTION:
- result = startSectionFilterHandler(*it);
- break;
- case DemuxFilterType::PES:
- result = startPesFilterHandler(*it);
- break;
- case DemuxFilterType::TS:
- result = startTsFilterHandler();
- break;
- case DemuxFilterType::AUDIO:
- case DemuxFilterType::VIDEO:
- result = startMediaFilterHandler(*it);
- break;
- case DemuxFilterType::RECORD:
- result = startRecordFilterHandler(*it);
- break;
- case DemuxFilterType::PCR:
- result = startPcrFilterHandler();
- break;
- default:
- return false;
+ if (mFilters[*it]->startFilterHandler() != Result::SUCCESS) {
+ return false;
}
}
- return result == Result::SUCCESS;
+ return true;
}
-void* Demux::__threadLoopFilter(void* threadArg) {
- Demux* const self = static_cast<Demux*>(((struct ThreadArgs*)threadArg)->user);
- self->filterThreadLoop(((struct ThreadArgs*)threadArg)->filterId);
- return 0;
+Result Demux::startFilterHandler(uint32_t filterId) {
+ return mFilters[filterId]->startFilterHandler();
}
-void* Demux::__threadLoopInput(void* user) {
- Demux* const self = static_cast<Demux*>(user);
- self->inputThreadLoop();
- return 0;
+void Demux::updateFilterOutput(uint16_t filterId, vector<uint8_t> data) {
+ mFilters[filterId]->updateFilterOutput(data);
}
-void Demux::filterThreadLoop(uint32_t filterId) {
- ALOGD("[Demux] filter %d threadLoop start.", filterId);
- std::lock_guard<std::mutex> lock(mFilterThreadLock);
- mFilterThreadRunning[filterId] = true;
-
- // For the first time of filter output, implementation needs to send the filter
- // Event Callback without waiting for the DATA_CONSUMED to init the process.
- while (mFilterThreadRunning[filterId]) {
- if (mFilterEvents[filterId].events.size() == 0) {
- ALOGD("[Demux] wait for filter data output.");
- usleep(1000 * 1000);
- continue;
- }
- // After successfully write, send a callback and wait for the read to be done
- mFilterCallbacks[filterId]->onFilterEvent(mFilterEvents[filterId]);
- mFilterEvents[filterId].events.resize(0);
- mFilterStatus[filterId] = DemuxFilterStatus::DATA_READY;
- mFilterCallbacks[filterId]->onFilterStatus(filterId, mFilterStatus[filterId]);
- break;
- }
-
- while (mFilterThreadRunning[filterId]) {
- uint32_t efState = 0;
- // We do not wait for the last round of writen data to be read to finish the thread
- // because the VTS can verify the reading itself.
- for (int i = 0; i < SECTION_WRITE_COUNT; i++) {
- while (mFilterThreadRunning[filterId]) {
- status_t status = mFilterEventFlags[filterId]->wait(
- static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED), &efState,
- WAIT_TIMEOUT, true /* retry on spurious wake */);
- if (status != OK) {
- ALOGD("[Demux] wait for data consumed");
- continue;
- }
- break;
- }
-
- if (mFilterCallbacks[filterId] == nullptr) {
- ALOGD("[Demux] filter %d does not hava callback. Ending thread", filterId);
- break;
- }
-
- maySendFilterStatusCallback(filterId);
-
- while (mFilterThreadRunning[filterId]) {
- std::lock_guard<std::mutex> lock(mFilterEventLock);
- if (mFilterEvents[filterId].events.size() == 0) {
- continue;
- }
- // After successfully write, send a callback and wait for the read to be done
- mFilterCallbacks[filterId]->onFilterEvent(mFilterEvents[filterId]);
- mFilterEvents[filterId].events.resize(0);
- break;
- }
- // We do not wait for the last read to be done
- // VTS can verify the read result itself.
- if (i == SECTION_WRITE_COUNT - 1) {
- ALOGD("[Demux] filter %d writing done. Ending thread", filterId);
- break;
- }
- }
- mFilterThreadRunning[filterId] = false;
- }
-
- ALOGD("[Demux] filter thread ended.");
-}
-
-void Demux::inputThreadLoop() {
- ALOGD("[Demux] input threadLoop start.");
- std::lock_guard<std::mutex> lock(mInputThreadLock);
- mInputThreadRunning = true;
-
- while (mInputThreadRunning) {
- uint32_t efState = 0;
- status_t status =
- mInputEventFlag->wait(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY),
- &efState, WAIT_TIMEOUT, true /* retry on spurious wake */);
- if (status != OK) {
- ALOGD("[Demux] wait for data ready on the input FMQ");
- continue;
- }
- // Our current implementation filter the data and write it into the filter FMQ immediately
- // after the DATA_READY from the VTS/framework
- if (!readInputFMQ() || !startFilterDispatcher()) {
- ALOGD("[Demux] input data failed to be filtered. Ending thread");
- break;
- }
-
- maySendInputStatusCallback();
- }
-
- mInputThreadRunning = false;
- ALOGD("[Demux] input thread ended.");
-}
-
-void Demux::maySendInputStatusCallback() {
- std::lock_guard<std::mutex> lock(mInputStatusLock);
- int availableToRead = mInputMQ->availableToRead();
- int availableToWrite = mInputMQ->availableToWrite();
-
- DemuxInputStatus newStatus =
- checkInputStatusChange(availableToWrite, availableToRead, mInputSettings.highThreshold,
- mInputSettings.lowThreshold);
- if (mIntputStatus != newStatus) {
- mInputCallback->onInputStatus(newStatus);
- mIntputStatus = newStatus;
- }
-}
-
-void Demux::maySendFilterStatusCallback(uint32_t filterId) {
- std::lock_guard<std::mutex> lock(mFilterStatusLock);
- int availableToRead = mFilterMQs[filterId]->availableToRead();
- int availableToWrite = mFilterMQs[filterId]->availableToWrite();
- int fmqSize = mFilterMQs[filterId]->getQuantumCount();
-
- DemuxFilterStatus newStatus =
- checkFilterStatusChange(filterId, availableToWrite, availableToRead,
- ceil(fmqSize * 0.75), ceil(fmqSize * 0.25));
- if (mFilterStatus[filterId] != newStatus) {
- mFilterCallbacks[filterId]->onFilterStatus(filterId, newStatus);
- mFilterStatus[filterId] = newStatus;
- }
-}
-
-DemuxInputStatus Demux::checkInputStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
- uint32_t highThreshold, uint32_t lowThreshold) {
- if (availableToWrite == 0) {
- return DemuxInputStatus::SPACE_FULL;
- } else if (availableToRead > highThreshold) {
- return DemuxInputStatus::SPACE_ALMOST_FULL;
- } else if (availableToRead < lowThreshold) {
- return DemuxInputStatus::SPACE_ALMOST_EMPTY;
- } else if (availableToRead == 0) {
- return DemuxInputStatus::SPACE_EMPTY;
- }
- return mIntputStatus;
-}
-
-DemuxFilterStatus Demux::checkFilterStatusChange(uint32_t filterId, uint32_t availableToWrite,
- uint32_t availableToRead, uint32_t highThreshold,
- uint32_t lowThreshold) {
- if (availableToWrite == 0) {
- return DemuxFilterStatus::OVERFLOW;
- } else if (availableToRead > highThreshold) {
- return DemuxFilterStatus::HIGH_WATER;
- } else if (availableToRead < lowThreshold) {
- return DemuxFilterStatus::LOW_WATER;
- }
- return mFilterStatus[filterId];
+uint16_t Demux::getFilterTpid(uint32_t filterId) {
+ return mFilters[filterId]->getTpid();
}
Result Demux::startBroadcastInputLoop() {
@@ -876,6 +232,7 @@
for (int i = 0; i < writePacketAmount; i++) {
inputData.read(buffer, packetSize);
if (!inputData) {
+ mKeepFetchingDataFromFrontend = false;
mBroadcastInputThreadRunning = false;
break;
}
@@ -888,7 +245,7 @@
startTsFilter(byteBuffer);
}
startFilterDispatcher();
- sleep(1);
+ usleep(100);
}
}
@@ -898,6 +255,7 @@
}
void Demux::stopBroadcastInput() {
+ ALOGD("[Demux] stop frontend on demux");
mKeepFetchingDataFromFrontend = false;
mBroadcastInputThreadRunning = false;
std::lock_guard<std::mutex> lock(mBroadcastInputThreadLock);
diff --git a/tv/tuner/1.0/default/Demux.h b/tv/tuner/1.0/default/Demux.h
index ba0b9b0..a9756cc 100644
--- a/tv/tuner/1.0/default/Demux.h
+++ b/tv/tuner/1.0/default/Demux.h
@@ -21,7 +21,10 @@
#include <fmq/MessageQueue.h>
#include <math.h>
#include <set>
+#include "Dvr.h"
+#include "Filter.h"
#include "Frontend.h"
+#include "TimeFilter.h"
#include "Tuner.h"
using namespace std;
@@ -38,13 +41,17 @@
using ::android::hardware::MessageQueue;
using ::android::hardware::MQDescriptorSync;
using ::android::hardware::tv::tuner::V1_0::IDemux;
-using ::android::hardware::tv::tuner::V1_0::IDemuxCallback;
+using ::android::hardware::tv::tuner::V1_0::IDvrCallback;
+using ::android::hardware::tv::tuner::V1_0::IFilterCallback;
using ::android::hardware::tv::tuner::V1_0::Result;
using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
-class Tuner;
+class Dvr;
+class Filter;
class Frontend;
+class TimeFilter;
+class Tuner;
class Demux : public IDemux {
public:
@@ -54,63 +61,27 @@
virtual Return<Result> setFrontendDataSource(uint32_t frontendId) override;
- virtual Return<Result> close() override;
+ virtual Return<void> openFilter(const DemuxFilterType& type, uint32_t bufferSize,
+ const sp<IFilterCallback>& cb, openFilter_cb _hidl_cb) override;
- virtual Return<void> addFilter(DemuxFilterType type, uint32_t bufferSize,
- const sp<IDemuxCallback>& cb, addFilter_cb _hidl_cb) override;
+ virtual Return<void> openTimeFilter(openTimeFilter_cb _hidl_cb) override;
- virtual Return<void> getFilterQueueDesc(uint32_t filterId,
- getFilterQueueDesc_cb _hidl_cb) override;
-
- virtual Return<Result> configureFilter(uint32_t filterId,
- const DemuxFilterSettings& settings) override;
-
- virtual Return<Result> startFilter(uint32_t filterId) override;
-
- virtual Return<Result> stopFilter(uint32_t filterId) override;
-
- virtual Return<Result> flushFilter(uint32_t filterId) override;
-
- virtual Return<Result> removeFilter(uint32_t filterId) override;
-
- virtual Return<void> getAvSyncHwId(uint32_t filterId, getAvSyncHwId_cb _hidl_cb) override;
+ virtual Return<void> getAvSyncHwId(const sp<IFilter>& filter,
+ getAvSyncHwId_cb _hidl_cb) override;
virtual Return<void> getAvSyncTime(AvSyncHwId avSyncHwId, getAvSyncTime_cb _hidl_cb) override;
- virtual Return<Result> addInput(uint32_t bufferSize, const sp<IDemuxCallback>& cb) override;
+ virtual Return<Result> close() override;
- virtual Return<void> getInputQueueDesc(getInputQueueDesc_cb _hidl_cb) override;
-
- virtual Return<Result> configureInput(const DemuxInputSettings& settings) override;
-
- virtual Return<Result> startInput() override;
-
- virtual Return<Result> stopInput() override;
-
- virtual Return<Result> flushInput() override;
-
- virtual Return<Result> removeInput() override;
-
- virtual Return<Result> addOutput(uint32_t bufferSize, const sp<IDemuxCallback>& cb) override;
-
- virtual Return<void> getOutputQueueDesc(getOutputQueueDesc_cb _hidl_cb) override;
-
- virtual Return<Result> configureOutput(const DemuxOutputSettings& settings) override;
-
- virtual Return<Result> attachOutputFilter(uint32_t filterId) override;
-
- virtual Return<Result> detachOutputFilter(uint32_t filterId) override;
-
- virtual Return<Result> startOutput() override;
-
- virtual Return<Result> stopOutput() override;
-
- virtual Return<Result> flushOutput() override;
-
- virtual Return<Result> removeOutput() override;
+ virtual Return<void> openDvr(DvrType type, uint32_t bufferSize, const sp<IDvrCallback>& cb,
+ openDvr_cb _hidl_cb) override;
// Functions interacts with Tuner Service
void stopBroadcastInput();
+ Result removeFilter(uint32_t filterId);
+ Result startFilterHandler(uint32_t filterId);
+ void updateFilterOutput(uint16_t filterId, vector<uint8_t> data);
+ uint16_t getFilterTpid(uint32_t filterId);
private:
// Tuner service
@@ -126,19 +97,9 @@
uint32_t filterId;
};
- /**
- * Filter handlers to handle the data filtering.
- * They are also responsible to write the filtered output into the filter FMQ
- * and update the filterEvent bound with the same filterId.
- */
- Result startSectionFilterHandler(uint32_t filterId);
- Result startPesFilterHandler(uint32_t filterId);
- Result startTsFilterHandler();
- Result startMediaFilterHandler(uint32_t filterId);
- Result startRecordFilterHandler(uint32_t filterId);
- Result startPcrFilterHandler();
- Result startFilterLoop(uint32_t filterId);
Result startBroadcastInputLoop();
+ static void* __threadLoopBroadcast(void* user);
+ void broadcastInputThreadLoop();
/**
* To create a FilterMQ with the the next available Filter ID.
@@ -147,32 +108,14 @@
*
* Return false is any of the above processes fails.
*/
- bool createFilterMQ(uint32_t bufferSize, uint32_t filterId);
- bool createMQ(FilterMQ* queue, EventFlag* eventFlag, uint32_t bufferSize);
void deleteEventFlag();
- bool writeDataToFilterMQ(const std::vector<uint8_t>& data, uint32_t filterId);
bool readDataFromMQ();
- bool writeSectionsAndCreateEvent(uint32_t filterId, vector<uint8_t> data);
- void maySendInputStatusCallback();
- void maySendFilterStatusCallback(uint32_t filterId);
- DemuxInputStatus checkInputStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
- uint32_t highThreshold, uint32_t lowThreshold);
- DemuxFilterStatus checkFilterStatusChange(uint32_t filterId, uint32_t availableToWrite,
- uint32_t availableToRead, uint32_t highThreshold,
- uint32_t lowThreshold);
/**
* A dispatcher to read and dispatch input data to all the started filters.
* Each filter handler handles the data filtering/output writing/filterEvent updating.
*/
- bool readInputFMQ();
- void startTsFilter(vector<uint8_t> data);
bool startFilterDispatcher();
- static void* __threadLoopFilter(void* data);
- static void* __threadLoopInput(void* user);
- static void* __threadLoopBroadcast(void* user);
- void filterThreadLoop(uint32_t filterId);
- void inputThreadLoop();
- void broadcastInputThreadLoop();
+ void startTsFilter(vector<uint8_t> data);
uint32_t mDemuxId;
/**
@@ -196,40 +139,13 @@
* A list of created FilterMQ ptrs.
* The array number is the filter ID.
*/
- vector<uint16_t> mFilterPids;
- vector<vector<uint8_t>> mFilterOutputs;
- vector<unique_ptr<FilterMQ>> mFilterMQs;
- vector<EventFlag*> mFilterEventFlags;
- vector<DemuxFilterEvent> mFilterEvents;
- unique_ptr<FilterMQ> mInputMQ;
- unique_ptr<FilterMQ> mOutputMQ;
- EventFlag* mInputEventFlag;
- EventFlag* mOutputEventFlag;
- /**
- * Demux callbacks used on filter events or IO buffer status
- */
- vector<sp<IDemuxCallback>> mFilterCallbacks;
- sp<IDemuxCallback> mInputCallback;
- sp<IDemuxCallback> mOutputCallback;
- bool mInputConfigured = false;
- bool mOutputConfigured = false;
- DemuxInputSettings mInputSettings;
- DemuxOutputSettings mOutputSettings;
+ std::map<uint32_t, sp<Filter>> mFilters;
// Thread handlers
- pthread_t mInputThread;
- pthread_t mOutputThread;
pthread_t mBroadcastInputThread;
- vector<pthread_t> mFilterThreads;
-
- // FMQ status local records
- DemuxInputStatus mIntputStatus;
- vector<DemuxFilterStatus> mFilterStatus;
/**
* If a specific filter's writing loop is still running
*/
- vector<bool> mFilterThreadRunning;
- bool mInputThreadRunning;
bool mBroadcastInputThreadRunning;
bool mKeepFetchingDataFromFrontend;
/**
@@ -237,28 +153,16 @@
*/
std::mutex mWriteLock;
/**
- * Lock to protect writes to the filter event
- */
- // TODO make each filter separate event lock
- std::mutex mFilterEventLock;
- /**
* Lock to protect writes to the input status
*/
- std::mutex mInputStatusLock;
- std::mutex mFilterStatusLock;
std::mutex mBroadcastInputThreadLock;
- std::mutex mFilterThreadLock;
- std::mutex mInputThreadLock;
- /**
- * How many times a filter should write
- * TODO make this dynamic/random/can take as a parameter
- */
- const uint16_t SECTION_WRITE_COUNT = 10;
// temp handle single PES filter
// TODO handle mulptiple Pes filters
int mPesSizeLeft = 0;
vector<uint8_t> mPesOutput;
+
+ const bool DEBUG_FILTER = false;
};
} // namespace implementation
diff --git a/tv/tuner/1.0/default/Descrambler.cpp b/tv/tuner/1.0/default/Descrambler.cpp
index 085f2c8..e3f5b22 100644
--- a/tv/tuner/1.0/default/Descrambler.cpp
+++ b/tv/tuner/1.0/default/Descrambler.cpp
@@ -50,13 +50,15 @@
return Result::SUCCESS;
}
-Return<Result> Descrambler::addPid(uint16_t /* pid */) {
+Return<Result> Descrambler::addPid(const DemuxPid& /* pid */,
+ const sp<IFilter>& /* optionalSourceFilter */) {
ALOGV("%s", __FUNCTION__);
return Result::SUCCESS;
}
-Return<Result> Descrambler::removePid(uint16_t /* pid */) {
+Return<Result> Descrambler::removePid(const DemuxPid& /* pid */,
+ const sp<IFilter>& /* optionalSourceFilter */) {
ALOGV("%s", __FUNCTION__);
return Result::SUCCESS;
diff --git a/tv/tuner/1.0/default/Descrambler.h b/tv/tuner/1.0/default/Descrambler.h
index 436adcf..c889820 100644
--- a/tv/tuner/1.0/default/Descrambler.h
+++ b/tv/tuner/1.0/default/Descrambler.h
@@ -40,9 +40,11 @@
virtual Return<Result> setKeyToken(const hidl_vec<uint8_t>& keyToken) override;
- virtual Return<Result> addPid(uint16_t pid) override;
+ virtual Return<Result> addPid(const DemuxPid& pid,
+ const sp<IFilter>& optionalSourceFilter) override;
- virtual Return<Result> removePid(uint16_t pid) override;
+ virtual Return<Result> removePid(const DemuxPid& pid,
+ const sp<IFilter>& optionalSourceFilter) override;
virtual Return<Result> close() override;
diff --git a/tv/tuner/1.0/default/Dvr.cpp b/tv/tuner/1.0/default/Dvr.cpp
new file mode 100644
index 0000000..eb38f90
--- /dev/null
+++ b/tv/tuner/1.0/default/Dvr.cpp
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.tv.tuner@1.0-Dvr"
+
+#include "Dvr.h"
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace tuner {
+namespace V1_0 {
+namespace implementation {
+
+#define WAIT_TIMEOUT 3000000000
+
+Dvr::Dvr() {}
+
+Dvr::Dvr(DvrType type, uint32_t bufferSize, const sp<IDvrCallback>& cb, sp<Demux> demux) {
+ mType = type;
+ mBufferSize = bufferSize;
+ mCallback = cb;
+ mDemux = demux;
+}
+
+Dvr::~Dvr() {}
+
+Return<void> Dvr::getQueueDesc(getQueueDesc_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ _hidl_cb(Result::SUCCESS, *mDvrMQ->getDesc());
+ return Void();
+}
+
+Return<Result> Dvr::configure(const DvrSettings& settings) {
+ ALOGV("%s", __FUNCTION__);
+
+ mDvrSettings = settings;
+ mDvrConfigured = true;
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Dvr::attachFilter(const sp<IFilter>& filter) {
+ ALOGV("%s", __FUNCTION__);
+
+ uint32_t filterId;
+ Result status;
+
+ filter->getId([&](Result result, uint32_t id) {
+ filterId = id;
+ status = result;
+ });
+
+ if (status != Result::SUCCESS) {
+ return status;
+ }
+
+ mFilters[filterId] = filter;
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Dvr::detachFilter(const sp<IFilter>& filter) {
+ ALOGV("%s", __FUNCTION__);
+
+ uint32_t filterId;
+ Result status;
+
+ filter->getId([&](Result result, uint32_t id) {
+ filterId = id;
+ status = result;
+ });
+
+ if (status != Result::SUCCESS) {
+ return status;
+ }
+
+ std::map<uint32_t, sp<IFilter>>::iterator it;
+
+ it = mFilters.find(filterId);
+ if (it != mFilters.end()) {
+ mFilters.erase(filterId);
+ }
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Dvr::start() {
+ ALOGV("%s", __FUNCTION__);
+
+ if (!mCallback) {
+ return Result::NOT_INITIALIZED;
+ }
+
+ if (!mDvrConfigured) {
+ return Result::INVALID_STATE;
+ }
+
+ if (mType == DvrType::PLAYBACK) {
+ pthread_create(&mDvrThread, NULL, __threadLoopPlayback, this);
+ pthread_setname_np(mDvrThread, "playback_waiting_loop");
+ } else if (mType == DvrType::RECORD) {
+ /*pthread_create(&mInputThread, NULL, __threadLoopInput, this);
+ pthread_setname_np(mInputThread, "playback_waiting_loop");*/
+ }
+
+ // TODO start another thread to send filter status callback to the framework
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Dvr::stop() {
+ ALOGV("%s", __FUNCTION__);
+
+ mDvrThreadRunning = false;
+
+ std::lock_guard<std::mutex> lock(mDvrThreadLock);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Dvr::flush() {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Dvr::close() {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+bool Dvr::createDvrMQ() {
+ ALOGV("%s", __FUNCTION__);
+
+ // Create a synchronized FMQ that supports blocking read/write
+ std::unique_ptr<DvrMQ> tmpDvrMQ =
+ std::unique_ptr<DvrMQ>(new (std::nothrow) DvrMQ(mBufferSize, true));
+ if (!tmpDvrMQ->isValid()) {
+ ALOGW("Failed to create FMQ of DVR");
+ return false;
+ }
+
+ mDvrMQ = std::move(tmpDvrMQ);
+
+ if (EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrEventFlag) != OK) {
+ return false;
+ }
+
+ return true;
+}
+
+void* Dvr::__threadLoopPlayback(void* user) {
+ Dvr* const self = static_cast<Dvr*>(user);
+ self->playbackThreadLoop();
+ return 0;
+}
+
+void Dvr::playbackThreadLoop() {
+ ALOGD("[Dvr] playback threadLoop start.");
+ std::lock_guard<std::mutex> lock(mDvrThreadLock);
+ mDvrThreadRunning = true;
+
+ while (mDvrThreadRunning) {
+ uint32_t efState = 0;
+ status_t status =
+ mDvrEventFlag->wait(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY),
+ &efState, WAIT_TIMEOUT, true /* retry on spurious wake */);
+ if (status != OK) {
+ ALOGD("[Dvr] wait for data ready on the playback FMQ");
+ continue;
+ }
+ // Our current implementation filter the data and write it into the filter FMQ immediately
+ // after the DATA_READY from the VTS/framework
+ if (!readPlaybackFMQ() || !startFilterDispatcher()) {
+ ALOGD("[Dvr] playback data failed to be filtered. Ending thread");
+ break;
+ }
+
+ maySendPlaybackStatusCallback();
+ }
+
+ mDvrThreadRunning = false;
+ ALOGD("[Dvr] playback thread ended.");
+}
+
+void Dvr::maySendPlaybackStatusCallback() {
+ std::lock_guard<std::mutex> lock(mPlaybackStatusLock);
+ int availableToRead = mDvrMQ->availableToRead();
+ int availableToWrite = mDvrMQ->availableToWrite();
+
+ PlaybackStatus newStatus = checkPlaybackStatusChange(availableToWrite, availableToRead,
+ mDvrSettings.playback().highThreshold,
+ mDvrSettings.playback().lowThreshold);
+ if (mPlaybackStatus != newStatus) {
+ mCallback->onPlaybackStatus(newStatus);
+ mPlaybackStatus = newStatus;
+ }
+}
+
+PlaybackStatus Dvr::checkPlaybackStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
+ uint32_t highThreshold, uint32_t lowThreshold) {
+ if (availableToWrite == 0) {
+ return PlaybackStatus::SPACE_FULL;
+ } else if (availableToRead > highThreshold) {
+ return PlaybackStatus::SPACE_ALMOST_FULL;
+ } else if (availableToRead < lowThreshold) {
+ return PlaybackStatus::SPACE_ALMOST_EMPTY;
+ } else if (availableToRead == 0) {
+ return PlaybackStatus::SPACE_EMPTY;
+ }
+ return mPlaybackStatus;
+}
+
+bool Dvr::readPlaybackFMQ() {
+ // Read playback data from the input FMQ
+ int size = mDvrMQ->availableToRead();
+ int playbackPacketSize = mDvrSettings.playback().packetSize;
+ vector<uint8_t> dataOutputBuffer;
+ dataOutputBuffer.resize(playbackPacketSize);
+
+ // Dispatch the packet to the PID matching filter output buffer
+ for (int i = 0; i < size / playbackPacketSize; i++) {
+ if (!mDvrMQ->read(dataOutputBuffer.data(), playbackPacketSize)) {
+ return false;
+ }
+ startTpidFilter(dataOutputBuffer);
+ }
+
+ return true;
+}
+
+void Dvr::startTpidFilter(vector<uint8_t> data) {
+ std::map<uint32_t, sp<IFilter>>::iterator it;
+ for (it = mFilters.begin(); it != mFilters.end(); it++) {
+ uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff));
+ if (DEBUG_DVR) {
+ ALOGW("[Dvr] start ts filter pid: %d", pid);
+ }
+ if (pid == mDemux->getFilterTpid(it->first)) {
+ mDemux->updateFilterOutput(it->first, data);
+ }
+ }
+}
+
+bool Dvr::startFilterDispatcher() {
+ std::map<uint32_t, sp<IFilter>>::iterator it;
+
+ // Handle the output data per filter type
+ for (it = mFilters.begin(); it != mFilters.end(); it++) {
+ if (mDemux->startFilterHandler(it->first) != Result::SUCCESS) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace tuner
+} // namespace tv
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/tv/tuner/1.0/default/Dvr.h b/tv/tuner/1.0/default/Dvr.h
new file mode 100644
index 0000000..fbb778c
--- /dev/null
+++ b/tv/tuner/1.0/default/Dvr.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_TV_TUNER_V1_0_DVR_H_
+#define ANDROID_HARDWARE_TV_TUNER_V1_0_DVR_H_
+
+#include <android/hardware/tv/tuner/1.0/IDvr.h>
+#include <fmq/MessageQueue.h>
+#include <math.h>
+#include <set>
+#include "Demux.h"
+#include "Frontend.h"
+#include "Tuner.h"
+
+using namespace std;
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace tuner {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::EventFlag;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::tv::tuner::V1_0::IDemux;
+using ::android::hardware::tv::tuner::V1_0::IDvrCallback;
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+using DvrMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+class Demux;
+class Filter;
+class Frontend;
+class Tuner;
+
+class Dvr : public IDvr {
+ public:
+ Dvr();
+
+ Dvr(DvrType type, uint32_t bufferSize, const sp<IDvrCallback>& cb, sp<Demux> demux);
+
+ ~Dvr();
+
+ virtual Return<void> getQueueDesc(getQueueDesc_cb _hidl_cb) override;
+
+ virtual Return<Result> configure(const DvrSettings& settings) override;
+
+ virtual Return<Result> attachFilter(const sp<IFilter>& filter) override;
+
+ virtual Return<Result> detachFilter(const sp<IFilter>& filter) override;
+
+ virtual Return<Result> start() override;
+
+ virtual Return<Result> stop() override;
+
+ virtual Return<Result> flush() override;
+
+ virtual Return<Result> close() override;
+
+ /**
+ * To create a DvrMQ and its Event Flag.
+ *
+ * Return false is any of the above processes fails.
+ */
+ bool createDvrMQ();
+
+ private:
+ // Demux service
+ sp<Demux> mDemux;
+
+ DvrType mType;
+ uint32_t mBufferSize;
+ sp<IDvrCallback> mCallback;
+ std::map<uint32_t, sp<IFilter>> mFilters;
+
+ void deleteEventFlag();
+ bool readDataFromMQ();
+ void maySendPlaybackStatusCallback();
+ void maySendRecordStatusCallback();
+ PlaybackStatus checkPlaybackStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
+ uint32_t highThreshold, uint32_t lowThreshold);
+ /**
+ * A dispatcher to read and dispatch input data to all the started filters.
+ * Each filter handler handles the data filtering/output writing/filterEvent updating.
+ */
+ bool readPlaybackFMQ();
+ void startTpidFilter(vector<uint8_t> data);
+ bool startFilterDispatcher();
+ static void* __threadLoopPlayback(void* user);
+ static void* __threadLoopBroadcast(void* user);
+ void playbackThreadLoop();
+ void broadcastInputThreadLoop();
+
+ unique_ptr<DvrMQ> mDvrMQ;
+ EventFlag* mDvrEventFlag;
+ /**
+ * Demux callbacks used on filter events or IO buffer status
+ */
+ bool mDvrConfigured = false;
+ DvrSettings mDvrSettings;
+
+ // Thread handlers
+ pthread_t mDvrThread;
+ pthread_t mBroadcastInputThread;
+
+ // FMQ status local records
+ PlaybackStatus mPlaybackStatus;
+ /**
+ * If a specific filter's writing loop is still running
+ */
+ bool mDvrThreadRunning;
+ bool mBroadcastInputThreadRunning;
+ bool mKeepFetchingDataFromFrontend;
+ /**
+ * Lock to protect writes to the FMQs
+ */
+ std::mutex mWriteLock;
+ /**
+ * Lock to protect writes to the input status
+ */
+ std::mutex mPlaybackStatusLock;
+ std::mutex mBroadcastInputThreadLock;
+ std::mutex mDvrThreadLock;
+
+ const bool DEBUG_DVR = false;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace tuner
+} // namespace tv
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_TV_TUNER_V1_0_DVR_H_
\ No newline at end of file
diff --git a/tv/tuner/1.0/default/Filter.cpp b/tv/tuner/1.0/default/Filter.cpp
new file mode 100644
index 0000000..3d8a977
--- /dev/null
+++ b/tv/tuner/1.0/default/Filter.cpp
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.tv.tuner@1.0-Filter"
+
+#include "Filter.h"
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace tuner {
+namespace V1_0 {
+namespace implementation {
+
+#define WAIT_TIMEOUT 3000000000
+
+Filter::Filter() {}
+
+Filter::Filter(DemuxFilterType type, uint32_t filterId, uint32_t bufferSize,
+ const sp<IFilterCallback>& cb, sp<Demux> demux) {
+ mType = type;
+ mFilterId = filterId;
+ mBufferSize = bufferSize;
+ mCallback = cb;
+ mDemux = demux;
+}
+
+Filter::~Filter() {}
+
+Return<void> Filter::getId(getId_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ _hidl_cb(Result::SUCCESS, mFilterId);
+ return Void();
+}
+
+Return<Result> Filter::setDataSource(const sp<IFilter>& filter) {
+ ALOGV("%s", __FUNCTION__);
+
+ mDataSource = filter;
+ mIsDataSourceDemux = false;
+
+ return Result::SUCCESS;
+}
+
+Return<void> Filter::getQueueDesc(getQueueDesc_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ _hidl_cb(Result::SUCCESS, *mFilterMQ->getDesc());
+ return Void();
+}
+
+Return<Result> Filter::configure(const DemuxFilterSettings& settings) {
+ ALOGV("%s", __FUNCTION__);
+
+ mFilterSettings = settings;
+ switch (mType.mainType) {
+ case DemuxFilterMainType::TS:
+ mTpid = settings.ts().tpid;
+ break;
+ case DemuxFilterMainType::MMTP:
+ /*mmtpSettings*/
+ break;
+ case DemuxFilterMainType::IP:
+ /*ipSettings*/
+ break;
+ case DemuxFilterMainType::TLV:
+ /*tlvSettings*/
+ break;
+ case DemuxFilterMainType::ALP:
+ /*alpSettings*/
+ break;
+ default:
+ break;
+ }
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Filter::start() {
+ ALOGV("%s", __FUNCTION__);
+
+ return startFilterLoop();
+}
+
+Return<Result> Filter::stop() {
+ ALOGV("%s", __FUNCTION__);
+
+ mFilterThreadRunning = false;
+
+ std::lock_guard<std::mutex> lock(mFilterThreadLock);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Filter::flush() {
+ ALOGV("%s", __FUNCTION__);
+
+ // temp implementation to flush the FMQ
+ int size = mFilterMQ->availableToRead();
+ char* buffer = new char[size];
+ mFilterMQ->read((unsigned char*)&buffer[0], size);
+ delete[] buffer;
+ mFilterStatus = DemuxFilterStatus::DATA_READY;
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Filter::close() {
+ ALOGV("%s", __FUNCTION__);
+
+ return mDemux->removeFilter(mFilterId);
+}
+
+bool Filter::createFilterMQ() {
+ ALOGV("%s", __FUNCTION__);
+
+ // Create a synchronized FMQ that supports blocking read/write
+ std::unique_ptr<FilterMQ> tmpFilterMQ =
+ std::unique_ptr<FilterMQ>(new (std::nothrow) FilterMQ(mBufferSize, true));
+ if (!tmpFilterMQ->isValid()) {
+ ALOGW("Failed to create FMQ of filter with id: %d", mFilterId);
+ return false;
+ }
+
+ mFilterMQ = std::move(tmpFilterMQ);
+
+ if (EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterEventFlag) != OK) {
+ return false;
+ }
+
+ return true;
+}
+
+Result Filter::startFilterLoop() {
+ pthread_create(&mFilterThread, NULL, __threadLoopFilter, this);
+ pthread_setname_np(mFilterThread, "filter_waiting_loop");
+
+ return Result::SUCCESS;
+}
+
+void* Filter::__threadLoopFilter(void* user) {
+ Filter* const self = static_cast<Filter*>(user);
+ self->filterThreadLoop();
+ return 0;
+}
+
+void Filter::filterThreadLoop() {
+ ALOGD("[Filter] filter %d threadLoop start.", mFilterId);
+ std::lock_guard<std::mutex> lock(mFilterThreadLock);
+ mFilterThreadRunning = true;
+
+ // For the first time of filter output, implementation needs to send the filter
+ // Event Callback without waiting for the DATA_CONSUMED to init the process.
+ while (mFilterThreadRunning) {
+ if (mFilterEvent.events.size() == 0) {
+ ALOGD("[Filter] wait for filter data output.");
+ usleep(1000 * 1000);
+ continue;
+ }
+ // After successfully write, send a callback and wait for the read to be done
+ mCallback->onFilterEvent(mFilterEvent);
+ mFilterEvent.events.resize(0);
+ mFilterStatus = DemuxFilterStatus::DATA_READY;
+ mCallback->onFilterStatus(mFilterStatus);
+ break;
+ }
+
+ while (mFilterThreadRunning) {
+ uint32_t efState = 0;
+ // We do not wait for the last round of written data to be read to finish the thread
+ // because the VTS can verify the reading itself.
+ for (int i = 0; i < SECTION_WRITE_COUNT; i++) {
+ while (mFilterThreadRunning) {
+ status_t status = mFilterEventFlag->wait(
+ static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED), &efState,
+ WAIT_TIMEOUT, true /* retry on spurious wake */);
+ if (status != OK) {
+ ALOGD("[Filter] wait for data consumed");
+ continue;
+ }
+ break;
+ }
+
+ if (mCallback == nullptr) {
+ ALOGD("[Filter] filter %d does not hava callback. Ending thread", mFilterId);
+ break;
+ }
+
+ maySendFilterStatusCallback();
+
+ while (mFilterThreadRunning) {
+ std::lock_guard<std::mutex> lock(mFilterEventLock);
+ if (mFilterEvent.events.size() == 0) {
+ continue;
+ }
+ // After successfully write, send a callback and wait for the read to be done
+ mCallback->onFilterEvent(mFilterEvent);
+ mFilterEvent.events.resize(0);
+ break;
+ }
+ // We do not wait for the last read to be done
+ // VTS can verify the read result itself.
+ if (i == SECTION_WRITE_COUNT - 1) {
+ ALOGD("[Filter] filter %d writing done. Ending thread", mFilterId);
+ break;
+ }
+ }
+ mFilterThreadRunning = false;
+ }
+
+ ALOGD("[Filter] filter thread ended.");
+}
+
+void Filter::maySendFilterStatusCallback() {
+ std::lock_guard<std::mutex> lock(mFilterStatusLock);
+ int availableToRead = mFilterMQ->availableToRead();
+ int availableToWrite = mFilterMQ->availableToWrite();
+ int fmqSize = mFilterMQ->getQuantumCount();
+
+ DemuxFilterStatus newStatus = checkFilterStatusChange(
+ availableToWrite, availableToRead, ceil(fmqSize * 0.75), ceil(fmqSize * 0.25));
+ if (mFilterStatus != newStatus) {
+ mCallback->onFilterStatus(newStatus);
+ mFilterStatus = newStatus;
+ }
+}
+
+DemuxFilterStatus Filter::checkFilterStatusChange(uint32_t availableToWrite,
+ uint32_t availableToRead, uint32_t highThreshold,
+ uint32_t lowThreshold) {
+ if (availableToWrite == 0) {
+ return DemuxFilterStatus::OVERFLOW;
+ } else if (availableToRead > highThreshold) {
+ return DemuxFilterStatus::HIGH_WATER;
+ } else if (availableToRead < lowThreshold) {
+ return DemuxFilterStatus::LOW_WATER;
+ }
+ return mFilterStatus;
+}
+
+uint16_t Filter::getTpid() {
+ return mTpid;
+}
+
+void Filter::updateFilterOutput(vector<uint8_t> data) {
+ std::lock_guard<std::mutex> lock(mFilterOutputLock);
+ ALOGD("[Filter] handler output updated");
+ mFilterOutput.insert(mFilterOutput.end(), data.begin(), data.end());
+}
+
+Result Filter::startFilterHandler() {
+ std::lock_guard<std::mutex> lock(mFilterOutputLock);
+ switch (mType.mainType) {
+ case DemuxFilterMainType::TS:
+ switch (mType.subType.tsFilterType()) {
+ case DemuxTsFilterType::UNDEFINED:
+ break;
+ case DemuxTsFilterType::SECTION:
+ startSectionFilterHandler();
+ break;
+ case DemuxTsFilterType::PES:
+ startPesFilterHandler();
+ break;
+ case DemuxTsFilterType::TS:
+ startTsFilterHandler();
+ break;
+ case DemuxTsFilterType::AUDIO:
+ case DemuxTsFilterType::VIDEO:
+ startMediaFilterHandler();
+ break;
+ case DemuxTsFilterType::PCR:
+ startPcrFilterHandler();
+ break;
+ case DemuxTsFilterType::RECORD:
+ startRecordFilterHandler();
+ break;
+ }
+ break;
+ case DemuxFilterMainType::MMTP:
+ /*mmtpSettings*/
+ break;
+ case DemuxFilterMainType::IP:
+ /*ipSettings*/
+ break;
+ case DemuxFilterMainType::TLV:
+ /*tlvSettings*/
+ break;
+ case DemuxFilterMainType::ALP:
+ /*alpSettings*/
+ break;
+ default:
+ break;
+ }
+ return Result::SUCCESS;
+}
+
+Result Filter::startSectionFilterHandler() {
+ if (mFilterOutput.empty()) {
+ return Result::SUCCESS;
+ }
+ if (!writeSectionsAndCreateEvent(mFilterOutput)) {
+ ALOGD("[Filter] filter %d fails to write into FMQ. Ending thread", mFilterId);
+ return Result::UNKNOWN_ERROR;
+ }
+
+ mFilterOutput.clear();
+
+ return Result::SUCCESS;
+}
+
+Result Filter::startPesFilterHandler() {
+ std::lock_guard<std::mutex> lock(mFilterEventLock);
+ if (mFilterOutput.empty()) {
+ return Result::SUCCESS;
+ }
+
+ for (int i = 0; i < mFilterOutput.size(); i += 188) {
+ if (mPesSizeLeft == 0) {
+ uint32_t prefix = (mFilterOutput[i + 4] << 16) | (mFilterOutput[i + 5] << 8) |
+ mFilterOutput[i + 6];
+ ALOGD("[Filter] prefix %d", prefix);
+ if (prefix == 0x000001) {
+ // TODO handle mulptiple Pes filters
+ mPesSizeLeft = (mFilterOutput[i + 8] << 8) | mFilterOutput[i + 9];
+ mPesSizeLeft += 6;
+ ALOGD("[Filter] pes data length %d", mPesSizeLeft);
+ } else {
+ continue;
+ }
+ }
+
+ int endPoint = min(184, mPesSizeLeft);
+ // append data and check size
+ vector<uint8_t>::const_iterator first = mFilterOutput.begin() + i + 4;
+ vector<uint8_t>::const_iterator last = mFilterOutput.begin() + i + 4 + endPoint;
+ mPesOutput.insert(mPesOutput.end(), first, last);
+ // size does not match then continue
+ mPesSizeLeft -= endPoint;
+ ALOGD("[Filter] pes data left %d", mPesSizeLeft);
+ if (mPesSizeLeft > 0) {
+ continue;
+ }
+ // size match then create event
+ if (!writeDataToFilterMQ(mPesOutput)) {
+ ALOGD("[Filter] pes data write failed");
+ mFilterOutput.clear();
+ return Result::INVALID_STATE;
+ }
+ maySendFilterStatusCallback();
+ DemuxFilterPesEvent pesEvent;
+ pesEvent = {
+ // temp dump meta data
+ .streamId = mPesOutput[3],
+ .dataLength = static_cast<uint16_t>(mPesOutput.size()),
+ };
+ ALOGD("[Filter] assembled pes data length %d", pesEvent.dataLength);
+
+ int size = mFilterEvent.events.size();
+ mFilterEvent.events.resize(size + 1);
+ mFilterEvent.events[size].pes(pesEvent);
+ mPesOutput.clear();
+ }
+
+ mFilterOutput.clear();
+
+ return Result::SUCCESS;
+}
+
+Result Filter::startTsFilterHandler() {
+ // TODO handle starting TS filter
+ return Result::SUCCESS;
+}
+
+Result Filter::startMediaFilterHandler() {
+ DemuxFilterMediaEvent mediaEvent;
+ mediaEvent = {
+ // temp dump meta data
+ .pts = 0,
+ .dataLength = 530,
+ .avMemory = nullptr,
+ .isSecureMemory = false,
+ };
+ mFilterEvent.events.resize(1);
+ mFilterEvent.events[0].media(mediaEvent);
+
+ mFilterOutput.clear();
+ // TODO handle write FQM for media stream
+ return Result::SUCCESS;
+}
+
+Result Filter::startRecordFilterHandler() {
+ DemuxFilterTsRecordEvent tsRecordEvent;
+ tsRecordEvent.pid.tPid(0);
+ tsRecordEvent.indexMask.tsIndexMask(0x01);
+ mFilterEvent.events.resize(1);
+ mFilterEvent.events[0].tsRecord(tsRecordEvent);
+
+ mFilterOutput.clear();
+ return Result::SUCCESS;
+}
+
+Result Filter::startPcrFilterHandler() {
+ // TODO handle starting PCR filter
+ return Result::SUCCESS;
+}
+
+bool Filter::writeSectionsAndCreateEvent(vector<uint8_t> data) {
+ // TODO check how many sections has been read
+ ALOGD("[Filter] section hander");
+ std::lock_guard<std::mutex> lock(mFilterEventLock);
+ if (!writeDataToFilterMQ(data)) {
+ return false;
+ }
+ int size = mFilterEvent.events.size();
+ mFilterEvent.events.resize(size + 1);
+ DemuxFilterSectionEvent secEvent;
+ secEvent = {
+ // temp dump meta data
+ .tableId = 0,
+ .version = 1,
+ .sectionNum = 1,
+ .dataLength = static_cast<uint16_t>(data.size()),
+ };
+ mFilterEvent.events[size].section(secEvent);
+ return true;
+}
+
+bool Filter::writeDataToFilterMQ(const std::vector<uint8_t>& data) {
+ std::lock_guard<std::mutex> lock(mWriteLock);
+ if (mFilterMQ->write(data.data(), data.size())) {
+ return true;
+ }
+ return false;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace tuner
+} // namespace tv
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/tv/tuner/1.0/default/Filter.h b/tv/tuner/1.0/default/Filter.h
new file mode 100644
index 0000000..21d4297
--- /dev/null
+++ b/tv/tuner/1.0/default/Filter.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_TV_TUNER_V1_0_FILTER_H_
+#define ANDROID_HARDWARE_TV_TUNER_V1_0_FILTER_H_
+
+#include <android/hardware/tv/tuner/1.0/IFilter.h>
+#include <fmq/MessageQueue.h>
+#include <math.h>
+#include <set>
+#include "Demux.h"
+#include "Frontend.h"
+
+using namespace std;
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace tuner {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::EventFlag;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::tv::tuner::V1_0::IDemux;
+using ::android::hardware::tv::tuner::V1_0::IFilterCallback;
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+class Demux;
+
+class Filter : public IFilter {
+ public:
+ Filter();
+
+ Filter(DemuxFilterType type, uint32_t filterId, uint32_t bufferSize,
+ const sp<IFilterCallback>& cb, sp<Demux> demux);
+
+ ~Filter();
+
+ virtual Return<void> getId(getId_cb _hidl_cb) override;
+
+ virtual Return<Result> setDataSource(const sp<IFilter>& filter) override;
+
+ virtual Return<void> getQueueDesc(getQueueDesc_cb _hidl_cb) override;
+
+ virtual Return<Result> configure(const DemuxFilterSettings& settings) override;
+
+ virtual Return<Result> start() override;
+
+ virtual Return<Result> stop() override;
+
+ virtual Return<Result> flush() override;
+
+ virtual Return<Result> close() override;
+
+ /**
+ * To create a FilterMQ and its Event Flag.
+ *
+ * Return false is any of the above processes fails.
+ */
+ bool createFilterMQ();
+ uint16_t getTpid();
+ void updateFilterOutput(vector<uint8_t> data);
+ Result startFilterHandler();
+
+ private:
+ // Tuner service
+ sp<Demux> mDemux;
+ /**
+ * Filter callbacks used on filter events or FMQ status
+ */
+ sp<IFilterCallback> mCallback;
+
+ uint32_t mFilterId;
+ uint32_t mBufferSize;
+ DemuxFilterType mType;
+ DemuxFilterSettings mFilterSettings;
+
+ uint16_t mTpid;
+ sp<IFilter> mDataSource;
+ bool mIsDataSourceDemux = true;
+ vector<uint8_t> mFilterOutput;
+ unique_ptr<FilterMQ> mFilterMQ;
+ EventFlag* mFilterEventFlag;
+ DemuxFilterEvent mFilterEvent;
+
+ // Thread handlers
+ pthread_t mFilterThread;
+
+ // FMQ status local records
+ DemuxFilterStatus mFilterStatus;
+ /**
+ * If a specific filter's writing loop is still running
+ */
+ bool mFilterThreadRunning;
+ bool mKeepFetchingDataFromFrontend;
+
+ /**
+ * How many times a filter should write
+ * TODO make this dynamic/random/can take as a parameter
+ */
+ const uint16_t SECTION_WRITE_COUNT = 10;
+
+ /**
+ * Filter handlers to handle the data filtering.
+ * They are also responsible to write the filtered output into the filter FMQ
+ * and update the filterEvent bound with the same filterId.
+ */
+ Result startSectionFilterHandler();
+ Result startPesFilterHandler();
+ Result startTsFilterHandler();
+ Result startMediaFilterHandler();
+ Result startRecordFilterHandler();
+ Result startPcrFilterHandler();
+ Result startFilterLoop();
+
+ void deleteEventFlag();
+ bool writeDataToFilterMQ(const std::vector<uint8_t>& data);
+ bool readDataFromMQ();
+ bool writeSectionsAndCreateEvent(vector<uint8_t> data);
+ void maySendFilterStatusCallback();
+ DemuxFilterStatus checkFilterStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
+ uint32_t highThreshold, uint32_t lowThreshold);
+ /**
+ * A dispatcher to read and dispatch input data to all the started filters.
+ * Each filter handler handles the data filtering/output writing/filterEvent updating.
+ */
+ void startTsFilter(vector<uint8_t> data);
+ bool startFilterDispatcher();
+ static void* __threadLoopFilter(void* user);
+ void filterThreadLoop();
+
+ /**
+ * Lock to protect writes to the FMQs
+ */
+ std::mutex mWriteLock;
+ /**
+ * Lock to protect writes to the filter event
+ */
+ // TODO make each filter separate event lock
+ std::mutex mFilterEventLock;
+ /**
+ * Lock to protect writes to the input status
+ */
+ std::mutex mFilterStatusLock;
+ std::mutex mFilterThreadLock;
+ std::mutex mFilterOutputLock;
+
+ // temp handle single PES filter
+ // TODO handle mulptiple Pes filters
+ int mPesSizeLeft = 0;
+ vector<uint8_t> mPesOutput;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace tuner
+} // namespace tv
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_TV_TUNER_V1_0_FILTER_H_
\ No newline at end of file
diff --git a/tv/tuner/1.0/default/Frontend.h b/tv/tuner/1.0/default/Frontend.h
index 07fa7b9..eab43a3 100644
--- a/tv/tuner/1.0/default/Frontend.h
+++ b/tv/tuner/1.0/default/Frontend.h
@@ -75,7 +75,7 @@
FrontendType mType = FrontendType::UNDEFINED;
FrontendId mId = 0;
- const string FRONTEND_STREAM_FILE = "/vendor/etc/test1.ts";
+ const string FRONTEND_STREAM_FILE = "/vendor/etc/dumpTs3.ts";
string mSourceStreamFile;
std::ifstream mFrontendData;
};
diff --git a/tv/tuner/1.0/default/Lnb.cpp b/tv/tuner/1.0/default/Lnb.cpp
index 1446f7f..51931d6 100644
--- a/tv/tuner/1.0/default/Lnb.cpp
+++ b/tv/tuner/1.0/default/Lnb.cpp
@@ -30,19 +30,25 @@
Lnb::~Lnb() {}
-Return<Result> Lnb::setVoltage(FrontendLnbVoltage /* voltage */) {
+Return<Result> Lnb::setCallback(const sp<ILnbCallback>& /* callback */) {
ALOGV("%s", __FUNCTION__);
return Result::SUCCESS;
}
-Return<Result> Lnb::setTone(FrontendLnbTone /* tone */) {
+Return<Result> Lnb::setVoltage(LnbVoltage /* voltage */) {
ALOGV("%s", __FUNCTION__);
return Result::SUCCESS;
}
-Return<Result> Lnb::setSatellitePosition(FrontendLnbPosition /* position */) {
+Return<Result> Lnb::setTone(LnbTone /* tone */) {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> Lnb::setSatellitePosition(LnbPosition /* position */) {
ALOGV("%s", __FUNCTION__);
return Result::SUCCESS;
diff --git a/tv/tuner/1.0/default/Lnb.h b/tv/tuner/1.0/default/Lnb.h
index 4c251f7..f285cb9 100644
--- a/tv/tuner/1.0/default/Lnb.h
+++ b/tv/tuner/1.0/default/Lnb.h
@@ -29,20 +29,23 @@
namespace V1_0 {
namespace implementation {
-using ::android::hardware::tv::tuner::V1_0::FrontendLnbPosition;
-using ::android::hardware::tv::tuner::V1_0::FrontendLnbTone;
-using ::android::hardware::tv::tuner::V1_0::FrontendLnbVoltage;
+using ::android::hardware::tv::tuner::V1_0::ILnbCallback;
+using ::android::hardware::tv::tuner::V1_0::LnbPosition;
+using ::android::hardware::tv::tuner::V1_0::LnbTone;
+using ::android::hardware::tv::tuner::V1_0::LnbVoltage;
using ::android::hardware::tv::tuner::V1_0::Result;
class Lnb : public ILnb {
public:
Lnb();
- virtual Return<Result> setVoltage(FrontendLnbVoltage voltage) override;
+ virtual Return<Result> setCallback(const sp<ILnbCallback>& callback) override;
- virtual Return<Result> setTone(FrontendLnbTone tone) override;
+ virtual Return<Result> setVoltage(LnbVoltage voltage) override;
- virtual Return<Result> setSatellitePosition(FrontendLnbPosition position) override;
+ virtual Return<Result> setTone(LnbTone tone) override;
+
+ virtual Return<Result> setSatellitePosition(LnbPosition position) override;
virtual Return<Result> sendDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) override;
diff --git a/tv/tuner/1.0/default/TimeFilter.cpp b/tv/tuner/1.0/default/TimeFilter.cpp
new file mode 100644
index 0000000..0b1fd1c
--- /dev/null
+++ b/tv/tuner/1.0/default/TimeFilter.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.tv.tuner@1.0-TimeFilter"
+
+#include "TimeFilter.h"
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace tuner {
+namespace V1_0 {
+namespace implementation {
+
+TimeFilter::TimeFilter() {}
+
+TimeFilter::TimeFilter(sp<Demux> demux) {
+ mDemux = demux;
+}
+
+TimeFilter::~TimeFilter() {}
+
+Return<Result> TimeFilter::setTimeStamp(uint64_t /* timeStamp */) {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<Result> TimeFilter::clearTimeStamp() {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+Return<void> TimeFilter::getTimeStamp(getTimeStamp_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ uint64_t timeStamp = 0;
+
+ _hidl_cb(Result::SUCCESS, timeStamp);
+ return Void();
+}
+
+Return<void> TimeFilter::getSourceTime(getSourceTime_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ uint64_t time = 0;
+
+ _hidl_cb(Result::SUCCESS, time);
+ return Void();
+}
+
+Return<Result> TimeFilter::close() {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace tuner
+} // namespace tv
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/tv/tuner/1.0/default/TimeFilter.h b/tv/tuner/1.0/default/TimeFilter.h
new file mode 100644
index 0000000..7131df8
--- /dev/null
+++ b/tv/tuner/1.0/default/TimeFilter.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_TV_TUNER_V1_0_TIMEFILTER_H_
+#define ANDROID_HARDWARE_TV_TUNER_V1_0_TIMEFILTER_H_
+
+#include <android/hardware/tv/tuner/1.0/ITimeFilter.h>
+#include "Demux.h"
+
+using namespace std;
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace tuner {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::tv::tuner::V1_0::IDemux;
+using ::android::hardware::tv::tuner::V1_0::IFilterCallback;
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+class Demux;
+
+class TimeFilter : public ITimeFilter {
+ public:
+ TimeFilter();
+
+ TimeFilter(sp<Demux> demux);
+
+ ~TimeFilter();
+
+ virtual Return<Result> setTimeStamp(uint64_t timeStamp) override;
+
+ virtual Return<Result> clearTimeStamp() override;
+
+ virtual Return<void> getTimeStamp(getTimeStamp_cb _hidl_cb) override;
+
+ virtual Return<void> getSourceTime(getSourceTime_cb _hidl_cb) override;
+
+ virtual Return<Result> close() override;
+
+ private:
+ sp<Demux> mDemux;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace tuner
+} // namespace tv
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_TV_TUNER_V1_0_TIMEFILTER_H_
\ No newline at end of file