From 3ab6e1fbbfba51384577c0e60d77742e7f29c9d2 Mon Sep 17 00:00:00 2001 From: Amy Zhang Date: Thu, 9 Jul 2020 22:42:15 -0700 Subject: [PATCH 1/3] Enable Tuner HAL 1.1 interface The first feature to support in this new Tuner HAL is the 64-bit id. In the current CL, we support Demux and Filter ids in 64-bit format. More component ids will be suppored in 64-bit in the following CLs. Test: atest VtsHalTvTunerV1_1TargetTest Bug: b/159058358 Change-Id: If05eb9f0cd76935064b2e51ff3ff1a3a477e3bd1 --- .../compatibility_matrix.current.xml | 2 +- tv/tuner/1.1/Android.bp | 16 +++++++ tv/tuner/1.1/IFilter.hal | 43 +++++++++++++++++++ tv/tuner/1.1/ITuner.hal | 26 +++++++++++ 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 tv/tuner/1.1/Android.bp create mode 100644 tv/tuner/1.1/IFilter.hal create mode 100644 tv/tuner/1.1/ITuner.hal diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml index cfee298c6d..90ec76e1cc 100644 --- a/compatibility_matrices/compatibility_matrix.current.xml +++ b/compatibility_matrices/compatibility_matrix.current.xml @@ -482,7 +482,7 @@ android.hardware.tv.tuner - 1.0 + 1.0-1 ITuner default diff --git a/tv/tuner/1.1/Android.bp b/tv/tuner/1.1/Android.bp new file mode 100644 index 0000000000..c051ab0902 --- /dev/null +++ b/tv/tuner/1.1/Android.bp @@ -0,0 +1,16 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.tv.tuner@1.1", + root: "android.hardware", + srcs: [ + "IFilter.hal", + "ITuner.hal", + ], + interfaces: [ + "android.hidl.base@1.0", + "android.hidl.safe_union@1.0", + "android.hardware.tv.tuner@1.0", + ], + gen_java: false, +} diff --git a/tv/tuner/1.1/IFilter.hal b/tv/tuner/1.1/IFilter.hal new file mode 100644 index 0000000000..6c4d8a5a03 --- /dev/null +++ b/tv/tuner/1.1/IFilter.hal @@ -0,0 +1,43 @@ +/* + * Copyright 2020 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. + */ + +package android.hardware.tv.tuner@1.1; + +import @1.0::IFilter; +import @1.0::Result; + +/** + * The Filter is used to filter wanted data according to the filter's + * configuration. + * + * To access the v1.1 IFilter APIs, the implementation can cast the IFilter + * interface returned from the @1.0::IDemux openFilter into a v1.1 IFiler + * using V1_1::IFilter::castFrom(V1_0::IFilter). + */ +interface IFilter extends @1.0::IFilter { + /** + * Get the 64-bit filter Id. This id is 32-bit in Tuner HAL 1.0. + * + * It is used by the client to ask the hardware resource id for the filter. + * + * @return result Result status of the operation. + * SUCCESS if successful, + * INVALID_STATE if failed for wrong state. + * UNKNOWN_ERROR if failed for other reasons. + * @return filterId the hardware resource Id for the filter. + */ + getId64Bit() generates (Result result, uint64_t filterId); +}; diff --git a/tv/tuner/1.1/ITuner.hal b/tv/tuner/1.1/ITuner.hal new file mode 100644 index 0000000000..915fb85806 --- /dev/null +++ b/tv/tuner/1.1/ITuner.hal @@ -0,0 +1,26 @@ +/* + * Copyright 2020 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. + */ + +package android.hardware.tv.tuner@1.1; + +import @1.0::ITuner; +import @1.0::Result; + +/** + * Top level interface to manage Frontend, Demux and Decrambler hardware + * resources which are needed for Android TV. + */ +interface ITuner extends @1.0::ITuner {}; From bb94eeb456c28e05fc54c6b23a34ab386766b2c0 Mon Sep 17 00:00:00 2001 From: Amy Zhang Date: Thu, 9 Jul 2020 22:48:04 -0700 Subject: [PATCH 2/3] Add default implementation of Tuner HAL 1.1 This CL enables the android.hardware.tv.tuner@1.1-service with the default implementation of Tuner HAL 1.1 The 1.0 Tuner HAL interface implementation are identical to the 1.0 default implementation. The 1.1 new API implementation are added with *_1_1 suffix. Note that we cast all the uint32_t id in the 1.0 Hal implementation into uint64_t even calling the old 1.0 APIs. This makes sure the 1.1 default implementation internally only record 64-bit ids. Test: atest VtsHalTvTunerV1_1TargetTest Bug: b/159058358 Change-Id: Ic506376e520f03235010bc503e337c02d5735ec3 --- tv/tuner/1.1/default/Android.bp | 52 ++ tv/tuner/1.1/default/Demux.cpp | 395 ++++++++++ tv/tuner/1.1/default/Demux.h | 197 +++++ tv/tuner/1.1/default/Descrambler.cpp | 80 +++ tv/tuner/1.1/default/Descrambler.h | 62 ++ tv/tuner/1.1/default/Dvr.cpp | 498 +++++++++++++ tv/tuner/1.1/default/Dvr.h | 167 +++++ tv/tuner/1.1/default/Filter.cpp | 672 ++++++++++++++++++ tv/tuner/1.1/default/Filter.h | 214 ++++++ tv/tuner/1.1/default/Frontend.cpp | 284 ++++++++ tv/tuner/1.1/default/Frontend.h | 85 +++ tv/tuner/1.1/default/Lnb.cpp | 81 +++ tv/tuner/1.1/default/Lnb.h | 63 ++ tv/tuner/1.1/default/OWNERS | 4 + tv/tuner/1.1/default/TimeFilter.cpp | 87 +++ tv/tuner/1.1/default/TimeFilter.h | 70 ++ tv/tuner/1.1/default/Tuner.cpp | 259 +++++++ tv/tuner/1.1/default/Tuner.h | 94 +++ ...roid.hardware.tv.tuner@1.1-service-lazy.rc | 9 + ...oid.hardware.tv.tuner@1.1-service-lazy.xml | 11 + .../android.hardware.tv.tuner@1.1-service.rc | 6 + .../android.hardware.tv.tuner@1.1-service.xml | 11 + tv/tuner/1.1/default/service.cpp | 57 ++ 23 files changed, 3458 insertions(+) create mode 100644 tv/tuner/1.1/default/Android.bp create mode 100644 tv/tuner/1.1/default/Demux.cpp create mode 100644 tv/tuner/1.1/default/Demux.h create mode 100644 tv/tuner/1.1/default/Descrambler.cpp create mode 100644 tv/tuner/1.1/default/Descrambler.h create mode 100644 tv/tuner/1.1/default/Dvr.cpp create mode 100644 tv/tuner/1.1/default/Dvr.h create mode 100644 tv/tuner/1.1/default/Filter.cpp create mode 100644 tv/tuner/1.1/default/Filter.h create mode 100644 tv/tuner/1.1/default/Frontend.cpp create mode 100644 tv/tuner/1.1/default/Frontend.h create mode 100644 tv/tuner/1.1/default/Lnb.cpp create mode 100644 tv/tuner/1.1/default/Lnb.h create mode 100644 tv/tuner/1.1/default/OWNERS create mode 100644 tv/tuner/1.1/default/TimeFilter.cpp create mode 100644 tv/tuner/1.1/default/TimeFilter.h create mode 100644 tv/tuner/1.1/default/Tuner.cpp create mode 100644 tv/tuner/1.1/default/Tuner.h create mode 100644 tv/tuner/1.1/default/android.hardware.tv.tuner@1.1-service-lazy.rc create mode 100644 tv/tuner/1.1/default/android.hardware.tv.tuner@1.1-service-lazy.xml create mode 100644 tv/tuner/1.1/default/android.hardware.tv.tuner@1.1-service.rc create mode 100644 tv/tuner/1.1/default/android.hardware.tv.tuner@1.1-service.xml create mode 100644 tv/tuner/1.1/default/service.cpp diff --git a/tv/tuner/1.1/default/Android.bp b/tv/tuner/1.1/default/Android.bp new file mode 100644 index 0000000000..7e45864791 --- /dev/null +++ b/tv/tuner/1.1/default/Android.bp @@ -0,0 +1,52 @@ +cc_defaults { + name: "tuner_service_defaults@1.1", + defaults: ["hidl_defaults"], + 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", + ], + + compile_multilib: "first", + + shared_libs: [ + "android.hardware.tv.tuner@1.0", + "android.hardware.tv.tuner@1.1", + "android.hidl.memory@1.0", + "libcutils", + "libfmq", + "libhidlbase", + "libhidlmemory", + "libion", + "liblog", + "libstagefright_foundation", + "libutils", + ], + header_libs: [ + "media_plugin_headers", + ], +} + +cc_binary { + name: "android.hardware.tv.tuner@1.1-service", + vintf_fragments: ["android.hardware.tv.tuner@1.1-service.xml"], + defaults: ["tuner_service_defaults@1.1"], + init_rc: ["android.hardware.tv.tuner@1.1-service.rc"], +} + +cc_binary { + name: "android.hardware.tv.tuner@1.1-service-lazy", + vintf_fragments: ["android.hardware.tv.tuner@1.1-service-lazy.xml"], + overrides: ["android.hardware.tv.tuner@1.1-service"], + defaults: ["tuner_service_defaults@1.1"], + init_rc: ["android.hardware.tv.tuner@1.1-service-lazy.rc"], + cflags: ["-DLAZY_SERVICE"], +} diff --git a/tv/tuner/1.1/default/Demux.cpp b/tv/tuner/1.1/default/Demux.cpp new file mode 100644 index 0000000000..b6b029fdb4 --- /dev/null +++ b/tv/tuner/1.1/default/Demux.cpp @@ -0,0 +1,395 @@ +/* + * Copyright 2020 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.1-Demux" + +#include "Demux.h" +#include + +namespace android { +namespace hardware { +namespace tv { +namespace tuner { +namespace V1_0 { +namespace implementation { + +#define WAIT_TIMEOUT 3000000000 +Demux::Demux(uint32_t demuxId, sp tuner) { + mDemuxId = demuxId; + mTunerService = tuner; +} + +Demux::~Demux() {} + +Return Demux::setFrontendDataSource(uint32_t frontendId) { + ALOGV("%s", __FUNCTION__); + + if (mTunerService == nullptr) { + return Result::NOT_INITIALIZED; + } + + mFrontend = mTunerService->getFrontendById(frontendId); + + if (mFrontend == nullptr) { + return Result::INVALID_STATE; + } + + mTunerService->setFrontendAsDemuxSource(frontendId, mDemuxId); + + return Result::SUCCESS; +} + +Return Demux::openFilter(const DemuxFilterType& type, uint32_t bufferSize, + const sp& cb, openFilter_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + uint64_t filterId; + filterId = ++mLastUsedFilterId; + + if (cb == nullptr) { + ALOGW("[Demux] callback can't be null"); + _hidl_cb(Result::INVALID_ARGUMENT, new Filter()); + return Void(); + } + + sp filter = new Filter(type, filterId, bufferSize, cb, this); + + if (!filter->createFilterMQ()) { + _hidl_cb(Result::UNKNOWN_ERROR, filter); + return Void(); + } + + mFilters[filterId] = filter; + if (filter->isPcrFilter()) { + mPcrFilterIds.insert(filterId); + } + bool result = true; + if (!filter->isRecordFilter()) { + // Only save non-record filters for now. Record filters are saved when the + // IDvr.attacheFilter is called. + mPlaybackFilterIds.insert(filterId); + if (mDvrPlayback != nullptr) { + result = mDvrPlayback->addPlaybackFilter(filterId, filter); + } + } + + _hidl_cb(result ? Result::SUCCESS : Result::INVALID_ARGUMENT, filter); + return Void(); +} + +Return Demux::openTimeFilter(openTimeFilter_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + mTimeFilter = new TimeFilter(this); + + _hidl_cb(Result::SUCCESS, mTimeFilter); + return Void(); +} + +Return Demux::getAvSyncHwId(const sp& filter, getAvSyncHwId_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + uint32_t avSyncHwId = -1; + uint64_t id; + Result status; + + sp filter_v1_1 = V1_1::IFilter::castFrom(filter); + if (filter_v1_1 != NULL) { + filter_v1_1->getId64Bit([&](Result result, uint64_t filterId) { + id = filterId; + status = result; + }); + } else { + filter->getId([&](Result result, uint32_t filterId) { + id = filterId; + status = result; + }); + } + + if (status != Result::SUCCESS) { + ALOGE("[Demux] Can't get filter Id."); + _hidl_cb(Result::INVALID_STATE, avSyncHwId); + return Void(); + } + + if (!mFilters[id]->isMediaFilter()) { + ALOGE("[Demux] Given filter is not a media filter."); + _hidl_cb(Result::INVALID_ARGUMENT, avSyncHwId); + return Void(); + } + + if (!mPcrFilterIds.empty()) { + // Return the lowest pcr filter id in the default implementation as the av sync id + _hidl_cb(Result::SUCCESS, *mPcrFilterIds.begin()); + return Void(); + } + + ALOGE("[Demux] No PCR filter opened."); + _hidl_cb(Result::INVALID_STATE, avSyncHwId); + return Void(); +} + +Return Demux::getAvSyncTime(AvSyncHwId avSyncHwId, getAvSyncTime_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + uint64_t avSyncTime = -1; + if (mPcrFilterIds.empty()) { + _hidl_cb(Result::INVALID_STATE, avSyncTime); + return Void(); + } + if (avSyncHwId != *mPcrFilterIds.begin()) { + _hidl_cb(Result::INVALID_ARGUMENT, avSyncTime); + return Void(); + } + + _hidl_cb(Result::SUCCESS, avSyncTime); + return Void(); +} + +Return Demux::close() { + ALOGV("%s", __FUNCTION__); + + set::iterator it; + for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) { + mDvrPlayback->removePlaybackFilter(*it); + } + mPlaybackFilterIds.clear(); + mRecordFilterIds.clear(); + mFilters.clear(); + mLastUsedFilterId = -1; + + return Result::SUCCESS; +} + +Return Demux::openDvr(DvrType type, uint32_t bufferSize, const sp& cb, + openDvr_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + if (cb == nullptr) { + ALOGW("[Demux] DVR callback can't be null"); + _hidl_cb(Result::INVALID_ARGUMENT, new Dvr()); + return Void(); + } + + set::iterator it; + switch (type) { + case DvrType::PLAYBACK: + mDvrPlayback = new Dvr(type, bufferSize, cb, this); + if (!mDvrPlayback->createDvrMQ()) { + _hidl_cb(Result::UNKNOWN_ERROR, mDvrPlayback); + return Void(); + } + + for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) { + if (!mDvrPlayback->addPlaybackFilter(*it, mFilters[*it])) { + ALOGE("[Demux] Can't get filter info for DVR playback"); + _hidl_cb(Result::UNKNOWN_ERROR, mDvrPlayback); + return Void(); + } + } + + _hidl_cb(Result::SUCCESS, mDvrPlayback); + return Void(); + case DvrType::RECORD: + mDvrRecord = new Dvr(type, bufferSize, cb, this); + if (!mDvrRecord->createDvrMQ()) { + _hidl_cb(Result::UNKNOWN_ERROR, mDvrRecord); + return Void(); + } + + _hidl_cb(Result::SUCCESS, mDvrRecord); + return Void(); + default: + _hidl_cb(Result::INVALID_ARGUMENT, nullptr); + return Void(); + } +} + +Return Demux::connectCiCam(uint32_t ciCamId) { + ALOGV("%s", __FUNCTION__); + + mCiCamId = ciCamId; + + return Result::SUCCESS; +} + +Return Demux::disconnectCiCam() { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +Result Demux::removeFilter(uint64_t filterId) { + ALOGV("%s", __FUNCTION__); + + if (mDvrPlayback != nullptr) { + mDvrPlayback->removePlaybackFilter(filterId); + } + mPlaybackFilterIds.erase(filterId); + mRecordFilterIds.erase(filterId); + mFilters.erase(filterId); + + return Result::SUCCESS; +} + +void Demux::startBroadcastTsFilter(vector data) { + set::iterator it; + uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff)); + if (DEBUG_DEMUX) { + ALOGW("[Demux] start ts filter pid: %d", pid); + } + for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) { + if (pid == mFilters[*it]->getTpid()) { + mFilters[*it]->updateFilterOutput(data); + } + } +} + +void Demux::sendFrontendInputToRecord(vector data) { + set::iterator it; + if (DEBUG_DEMUX) { + ALOGW("[Demux] update record filter output"); + } + for (it = mRecordFilterIds.begin(); it != mRecordFilterIds.end(); it++) { + mFilters[*it]->updateRecordOutput(data); + } +} + +bool Demux::startBroadcastFilterDispatcher() { + set::iterator it; + + // Handle the output data per filter type + for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) { + if (mFilters[*it]->startFilterHandler() != Result::SUCCESS) { + return false; + } + } + + return true; +} + +bool Demux::startRecordFilterDispatcher() { + set::iterator it; + + for (it = mRecordFilterIds.begin(); it != mRecordFilterIds.end(); it++) { + if (mFilters[*it]->startRecordFilterHandler() != Result::SUCCESS) { + return false; + } + } + + return true; +} + +Result Demux::startFilterHandler(uint64_t filterId) { + return mFilters[filterId]->startFilterHandler(); +} + +void Demux::updateFilterOutput(uint64_t filterId, vector data) { + mFilters[filterId]->updateFilterOutput(data); +} + +void Demux::updateMediaFilterOutput(uint64_t filterId, vector data, uint64_t pts) { + updateFilterOutput(filterId, data); + mFilters[filterId]->updatePts(pts); +} + +uint16_t Demux::getFilterTpid(uint64_t filterId) { + return mFilters[filterId]->getTpid(); +} + +void Demux::startFrontendInputLoop() { + pthread_create(&mFrontendInputThread, NULL, __threadLoopFrontend, this); + pthread_setname_np(mFrontendInputThread, "frontend_input_thread"); +} + +void* Demux::__threadLoopFrontend(void* user) { + Demux* const self = static_cast(user); + self->frontendInputThreadLoop(); + return 0; +} + +void Demux::frontendInputThreadLoop() { + std::lock_guard lock(mFrontendInputThreadLock); + mFrontendInputThreadRunning = true; + + while (mFrontendInputThreadRunning) { + uint32_t efState = 0; + status_t status = mDvrPlayback->getDvrEventFlag()->wait( + static_cast(DemuxQueueNotifyBits::DATA_READY), &efState, WAIT_TIMEOUT, + true /* retry on spurious wake */); + if (status != OK) { + ALOGD("[Demux] wait for data ready on the playback FMQ"); + continue; + } + if (mDvrPlayback->getSettings().playback().dataFormat == DataFormat::ES) { + if (!mDvrPlayback->processEsDataOnPlayback(true /*isVirtualFrontend*/, mIsRecording)) { + ALOGE("[Demux] playback es data failed to be filtered. Ending thread"); + break; + } + } + // Our current implementation filter the data and write it into the filter FMQ immediately + // after the DATA_READY from the VTS/framework + if (!mDvrPlayback->readPlaybackFMQ(true /*isVirtualFrontend*/, mIsRecording) || + !mDvrPlayback->startFilterDispatcher(true /*isVirtualFrontend*/, mIsRecording)) { + ALOGE("[Demux] playback data failed to be filtered. Ending thread"); + break; + } + } + + mFrontendInputThreadRunning = false; + ALOGW("[Demux] Frontend Input thread end."); +} + +void Demux::stopFrontendInput() { + ALOGD("[Demux] stop frontend on demux"); + mKeepFetchingDataFromFrontend = false; + mFrontendInputThreadRunning = false; + std::lock_guard lock(mFrontendInputThreadLock); +} + +void Demux::setIsRecording(bool isRecording) { + mIsRecording = isRecording; +} + +bool Demux::attachRecordFilter(uint64_t filterId) { + if (mFilters[filterId] == nullptr || mDvrRecord == nullptr || + !mFilters[filterId]->isRecordFilter()) { + return false; + } + + mRecordFilterIds.insert(filterId); + mFilters[filterId]->attachFilterToRecord(mDvrRecord); + + return true; +} + +bool Demux::detachRecordFilter(uint64_t filterId) { + if (mFilters[filterId] == nullptr || mDvrRecord == nullptr) { + return false; + } + + mRecordFilterIds.erase(filterId); + mFilters[filterId]->detachFilterFromRecord(); + + return true; +} + +} // namespace implementation +} // namespace V1_0 +} // namespace tuner +} // namespace tv +} // namespace hardware +} // namespace android diff --git a/tv/tuner/1.1/default/Demux.h b/tv/tuner/1.1/default/Demux.h new file mode 100644 index 0000000000..62f916267f --- /dev/null +++ b/tv/tuner/1.1/default/Demux.h @@ -0,0 +1,197 @@ +/* + * Copyright 2020 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_1_DEMUX_H_ +#define ANDROID_HARDWARE_TV_TUNER_V1_1_DEMUX_H_ + +#include +#include +#include +#include "Dvr.h" +#include "Filter.h" +#include "Frontend.h" +#include "TimeFilter.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 FilterMQ = MessageQueue; + +class Dvr; +class Filter; +class Frontend; +class TimeFilter; +class Tuner; + +class Demux : public IDemux { + public: + Demux(uint32_t demuxId, sp tuner); + + ~Demux(); + + virtual Return setFrontendDataSource(uint32_t frontendId) override; + + virtual Return openFilter(const DemuxFilterType& type, uint32_t bufferSize, + const sp& cb, openFilter_cb _hidl_cb) override; + + virtual Return openTimeFilter(openTimeFilter_cb _hidl_cb) override; + + virtual Return getAvSyncHwId(const sp& filter, + getAvSyncHwId_cb _hidl_cb) override; + + virtual Return getAvSyncTime(AvSyncHwId avSyncHwId, getAvSyncTime_cb _hidl_cb) override; + + virtual Return close() override; + + virtual Return openDvr(DvrType type, uint32_t bufferSize, const sp& cb, + openDvr_cb _hidl_cb) override; + + virtual Return connectCiCam(uint32_t ciCamId) override; + + virtual Return disconnectCiCam() override; + + // Functions interacts with Tuner Service + void stopFrontendInput(); + Result removeFilter(uint64_t filterId); + bool attachRecordFilter(uint64_t filterId); + bool detachRecordFilter(uint64_t filterId); + Result startFilterHandler(uint64_t filterId); + void updateFilterOutput(uint64_t filterId, vector data); + void updateMediaFilterOutput(uint64_t filterId, vector data, uint64_t pts); + uint16_t getFilterTpid(uint64_t filterId); + void setIsRecording(bool isRecording); + void startFrontendInputLoop(); + + /** + * A dispatcher to read and dispatch input data to all the started filters. + * Each filter handler handles the data filtering/output writing/filterEvent updating. + * Note that recording filters are not included. + */ + bool startBroadcastFilterDispatcher(); + void startBroadcastTsFilter(vector data); + + void sendFrontendInputToRecord(vector data); + bool startRecordFilterDispatcher(); + + private: + // Tuner service + sp mTunerService; + + // Frontend source + sp mFrontend; + + // A struct that passes the arguments to a newly created filter thread + struct ThreadArgs { + Demux* user; + uint64_t filterId; + }; + + static void* __threadLoopFrontend(void* user); + void frontendInputThreadLoop(); + + /** + * To create a FilterMQ with the next available Filter ID. + * Creating Event Flag at the same time. + * Add the successfully created/saved FilterMQ into the local list. + * + * Return false is any of the above processes fails. + */ + void deleteEventFlag(); + bool readDataFromMQ(); + + uint32_t mDemuxId = -1; + uint32_t mCiCamId; + set mPcrFilterIds; + /** + * Record the last used filter id. Initial value is -1. + * Filter Id starts with 0. + */ + uint64_t mLastUsedFilterId = -1; + /** + * Record all the used playback filter Ids. + * Any removed filter id should be removed from this set. + */ + set mPlaybackFilterIds; + /** + * Record all the attached record filter Ids. + * Any removed filter id should be removed from this set. + */ + set mRecordFilterIds; + /** + * A list of created Filter sp. + * The array number is the filter ID. + */ + std::map> mFilters; + + /** + * Local reference to the opened Timer Filter instance. + */ + sp mTimeFilter; + + /** + * Local reference to the opened DVR object. + */ + sp mDvrPlayback; + sp mDvrRecord; + + // Thread handlers + pthread_t mFrontendInputThread; + /** + * If a specific filter's writing loop is still running + */ + bool mFrontendInputThreadRunning; + bool mKeepFetchingDataFromFrontend; + /** + * If the dvr recording is running. + */ + bool mIsRecording = false; + /** + * Lock to protect writes to the FMQs + */ + std::mutex mWriteLock; + /** + * Lock to protect writes to the input status + */ + std::mutex mFrontendInputThreadLock; + + // temp handle single PES filter + // TODO handle mulptiple Pes filters + int mPesSizeLeft = 0; + vector mPesOutput; + + const bool DEBUG_DEMUX = false; +}; + +} // namespace implementation +} // namespace V1_0 +} // namespace tuner +} // namespace tv +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_TV_TUNER_V1_1_DEMUX_H_ diff --git a/tv/tuner/1.1/default/Descrambler.cpp b/tv/tuner/1.1/default/Descrambler.cpp new file mode 100644 index 0000000000..1fbc780c34 --- /dev/null +++ b/tv/tuner/1.1/default/Descrambler.cpp @@ -0,0 +1,80 @@ +/* + * Copyright 2020 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.1-Descrambler" + +#include +#include + +#include "Descrambler.h" + +namespace android { +namespace hardware { +namespace tv { +namespace tuner { +namespace V1_0 { +namespace implementation { + +Descrambler::Descrambler() {} + +Descrambler::~Descrambler() {} + +Return Descrambler::setDemuxSource(uint32_t demuxId) { + ALOGV("%s", __FUNCTION__); + if (mDemuxSet) { + ALOGW("[ WARN ] Descrambler has already been set with a demux id %" PRIu32, + mSourceDemuxId); + return Result::INVALID_STATE; + } + mDemuxSet = true; + mSourceDemuxId = static_cast(demuxId); + + return Result::SUCCESS; +} + +Return Descrambler::setKeyToken(const hidl_vec& /* keyToken */) { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +Return Descrambler::addPid(const DemuxPid& /* pid */, + const sp& /* optionalSourceFilter */) { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +Return Descrambler::removePid(const DemuxPid& /* pid */, + const sp& /* optionalSourceFilter */) { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +Return Descrambler::close() { + ALOGV("%s", __FUNCTION__); + mDemuxSet = false; + + return Result::SUCCESS; +} + +} // namespace implementation +} // namespace V1_0 +} // namespace tuner +} // namespace tv +} // namespace hardware +} // namespace android diff --git a/tv/tuner/1.1/default/Descrambler.h b/tv/tuner/1.1/default/Descrambler.h new file mode 100644 index 0000000000..ffc284ddde --- /dev/null +++ b/tv/tuner/1.1/default/Descrambler.h @@ -0,0 +1,62 @@ +/* + * Copyright 2020 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_1_DESCRAMBLER_H_ +#define ANDROID_HARDWARE_TV_TUNER_V1_1_DESCRAMBLER_H_ + +#include +#include +#include + +using namespace std; + +namespace android { +namespace hardware { +namespace tv { +namespace tuner { +namespace V1_0 { +namespace implementation { + +class Descrambler : public IDescrambler { + public: + Descrambler(); + + virtual Return setDemuxSource(uint32_t demuxId) override; + + virtual Return setKeyToken(const hidl_vec& keyToken) override; + + virtual Return addPid(const DemuxPid& pid, + const sp& optionalSourceFilter) override; + + virtual Return removePid(const DemuxPid& pid, + const sp& optionalSourceFilter) override; + + virtual Return close() override; + + private: + virtual ~Descrambler(); + uint32_t mSourceDemuxId; + bool mDemuxSet = false; +}; + +} // namespace implementation +} // namespace V1_0 +} // namespace tuner +} // namespace tv +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_TV_TUNER_V1_DESCRAMBLER_H_ diff --git a/tv/tuner/1.1/default/Dvr.cpp b/tv/tuner/1.1/default/Dvr.cpp new file mode 100644 index 0000000000..02d6a42e58 --- /dev/null +++ b/tv/tuner/1.1/default/Dvr.cpp @@ -0,0 +1,498 @@ +/* + * Copyright 2020 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.1-Dvr" + +#include "Dvr.h" +#include + +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& cb, sp demux) { + mType = type; + mBufferSize = bufferSize; + mCallback = cb; + mDemux = demux; +} + +Dvr::~Dvr() {} + +Return Dvr::getQueueDesc(getQueueDesc_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + _hidl_cb(Result::SUCCESS, *mDvrMQ->getDesc()); + return Void(); +} + +Return Dvr::configure(const DvrSettings& settings) { + ALOGV("%s", __FUNCTION__); + + mDvrSettings = settings; + mDvrConfigured = true; + + return Result::SUCCESS; +} + +Return Dvr::attachFilter(const sp& filter) { + ALOGV("%s", __FUNCTION__); + + uint64_t filterId; + Result status; + + sp filter_v1_1 = V1_1::IFilter::castFrom(filter); + if (filter_v1_1 != NULL) { + filter_v1_1->getId64Bit([&](Result result, uint64_t id) { + filterId = id; + status = result; + }); + } else { + filter->getId([&](Result result, uint32_t id) { + filterId = id; + status = result; + }); + } + + if (status != Result::SUCCESS) { + return status; + } + + // TODO check if the attached filter is a record filter + if (!mDemux->attachRecordFilter(filterId)) { + return Result::INVALID_ARGUMENT; + } + + return Result::SUCCESS; +} + +Return Dvr::detachFilter(const sp& filter) { + ALOGV("%s", __FUNCTION__); + + uint64_t filterId; + Result status; + + sp filter_v1_1 = V1_1::IFilter::castFrom(filter); + if (filter_v1_1 != NULL) { + filter_v1_1->getId64Bit([&](Result result, uint64_t id) { + filterId = id; + status = result; + }); + } else { + filter->getId([&](Result result, uint32_t id) { + filterId = id; + status = result; + }); + } + + if (status != Result::SUCCESS) { + return status; + } + + if (!mDemux->detachRecordFilter(filterId)) { + return Result::INVALID_ARGUMENT; + } + + return Result::SUCCESS; +} + +Return 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) { + mRecordStatus = RecordStatus::DATA_READY; + mDemux->setIsRecording(mType == DvrType::RECORD); + } + + // TODO start another thread to send filter status callback to the framework + + return Result::SUCCESS; +} + +Return Dvr::stop() { + ALOGV("%s", __FUNCTION__); + + mDvrThreadRunning = false; + + lock_guard lock(mDvrThreadLock); + + mIsRecordStarted = false; + mDemux->setIsRecording(false); + + return Result::SUCCESS; +} + +Return Dvr::flush() { + ALOGV("%s", __FUNCTION__); + + mRecordStatus = RecordStatus::DATA_READY; + + return Result::SUCCESS; +} + +Return Dvr::close() { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +bool Dvr::createDvrMQ() { + ALOGV("%s", __FUNCTION__); + + // Create a synchronized FMQ that supports blocking read/write + unique_ptr tmpDvrMQ = unique_ptr(new (nothrow) DvrMQ(mBufferSize, true)); + if (!tmpDvrMQ->isValid()) { + ALOGW("[Dvr] Failed to create FMQ of DVR"); + return false; + } + + mDvrMQ = move(tmpDvrMQ); + + if (EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrEventFlag) != OK) { + return false; + } + + return true; +} + +EventFlag* Dvr::getDvrEventFlag() { + return mDvrEventFlag; +} + +void* Dvr::__threadLoopPlayback(void* user) { + Dvr* const self = static_cast(user); + self->playbackThreadLoop(); + return 0; +} + +void Dvr::playbackThreadLoop() { + ALOGD("[Dvr] playback threadLoop start."); + lock_guard lock(mDvrThreadLock); + mDvrThreadRunning = true; + + while (mDvrThreadRunning) { + uint32_t efState = 0; + status_t status = + mDvrEventFlag->wait(static_cast(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; + } + + if (mDvrSettings.playback().dataFormat == DataFormat::ES) { + if (!processEsDataOnPlayback(false /*isVirtualFrontend*/, false /*isRecording*/)) { + ALOGE("[Dvr] playback es data failed to be filtered. Ending thread"); + break; + } + maySendPlaybackStatusCallback(); + } + // Our current implementation filter the data and write it into the filter FMQ immediately + // after the DATA_READY from the VTS/framework + if (!readPlaybackFMQ(false /*isVirtualFrontend*/, false /*isRecording*/) || + !startFilterDispatcher(false /*isVirtualFrontend*/, false /*isRecording*/)) { + ALOGE("[Dvr] playback data failed to be filtered. Ending thread"); + break; + } + + maySendPlaybackStatusCallback(); + } + + mDvrThreadRunning = false; + ALOGD("[Dvr] playback thread ended."); +} + +void Dvr::maySendPlaybackStatusCallback() { + lock_guard 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(bool isVirtualFrontend, bool isRecording) { + // Read playback data from the input FMQ + int size = mDvrMQ->availableToRead(); + int playbackPacketSize = mDvrSettings.playback().packetSize; + vector 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; + } + if (isVirtualFrontend) { + if (isRecording) { + mDemux->sendFrontendInputToRecord(dataOutputBuffer); + } else { + mDemux->startBroadcastTsFilter(dataOutputBuffer); + } + } else { + startTpidFilter(dataOutputBuffer); + } + } + + return true; +} + +bool Dvr::processEsDataOnPlayback(bool isVirtualFrontend, bool isRecording) { + // Read ES from the DVR FMQ + // Note that currently we only provides ES with metaData in a specific format to be parsed. + // The ES size should be smaller than the Playback FMQ size to avoid reading truncated data. + int size = mDvrMQ->availableToRead(); + vector dataOutputBuffer; + dataOutputBuffer.resize(size); + if (!mDvrMQ->read(dataOutputBuffer.data(), size)) { + return false; + } + + int metaDataSize = size; + int totalFrames = 0; + int videoEsDataSize = 0; + int audioEsDataSize = 0; + int audioPid = 0; + int videoPid = 0; + + vector esMeta; + int videoReadPointer = 0; + int audioReadPointer = 0; + int frameCount = 0; + // Get meta data from the es + for (int i = 0; i < metaDataSize; i++) { + switch (dataOutputBuffer[i]) { + case 'm': + metaDataSize = 0; + getMetaDataValue(i, dataOutputBuffer.data(), metaDataSize); + videoReadPointer = metaDataSize; + continue; + case 'l': + getMetaDataValue(i, dataOutputBuffer.data(), totalFrames); + esMeta.resize(totalFrames); + continue; + case 'V': + getMetaDataValue(i, dataOutputBuffer.data(), videoEsDataSize); + audioReadPointer = metaDataSize + videoEsDataSize; + continue; + case 'A': + getMetaDataValue(i, dataOutputBuffer.data(), audioEsDataSize); + continue; + case 'p': + if (dataOutputBuffer[++i] == 'a') { + getMetaDataValue(i, dataOutputBuffer.data(), audioPid); + } else if (dataOutputBuffer[i] == 'v') { + getMetaDataValue(i, dataOutputBuffer.data(), videoPid); + } + continue; + case 'v': + case 'a': + if (dataOutputBuffer[i + 1] != ',') { + ALOGE("[Dvr] Invalid format meta data."); + return false; + } + esMeta[frameCount] = { + .isAudio = dataOutputBuffer[i] == 'a' ? true : false, + }; + i += 5; // Move to Len + getMetaDataValue(i, dataOutputBuffer.data(), esMeta[frameCount].len); + if (esMeta[frameCount].isAudio) { + esMeta[frameCount].startIndex = audioReadPointer; + audioReadPointer += esMeta[frameCount].len; + } else { + esMeta[frameCount].startIndex = videoReadPointer; + videoReadPointer += esMeta[frameCount].len; + } + i += 4; // move to PTS + getMetaDataValue(i, dataOutputBuffer.data(), esMeta[frameCount].pts); + frameCount++; + continue; + default: + continue; + } + } + + if (frameCount != totalFrames) { + ALOGE("[Dvr] Invalid meta data, frameCount=%d, totalFrames reported=%d", frameCount, + totalFrames); + return false; + } + + if (metaDataSize + audioEsDataSize + videoEsDataSize != size) { + ALOGE("[Dvr] Invalid meta data, metaSize=%d, videoSize=%d, audioSize=%d, totolSize=%d", + metaDataSize, videoEsDataSize, audioEsDataSize, size); + return false; + } + + // Read es raw data from the FMQ per meta data built previously + vector frameData; + map>::iterator it; + int pid = 0; + for (int i = 0; i < totalFrames; i++) { + frameData.resize(esMeta[i].len); + pid = esMeta[i].isAudio ? audioPid : videoPid; + memcpy(dataOutputBuffer.data() + esMeta[i].startIndex, frameData.data(), esMeta[i].len); + // Send to the media filter + if (isVirtualFrontend && isRecording) { + // TODO validate record + mDemux->sendFrontendInputToRecord(frameData); + } else { + for (it = mFilters.begin(); it != mFilters.end(); it++) { + if (pid == mDemux->getFilterTpid(it->first)) { + mDemux->updateMediaFilterOutput(it->first, frameData, + static_cast(esMeta[i].pts)); + startFilterDispatcher(isVirtualFrontend, isRecording); + } + } + } + } + + return true; +} + +void Dvr::getMetaDataValue(int& index, uint8_t* dataOutputBuffer, int& value) { + index += 2; // Move the pointer across the ":" to the value + while (dataOutputBuffer[index] != ',' && dataOutputBuffer[index] != '\n') { + value = ((dataOutputBuffer[index++] - 48) + value * 10); + } +} + +void Dvr::startTpidFilter(vector data) { + map>::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(bool isVirtualFrontend, bool isRecording) { + if (isVirtualFrontend) { + if (isRecording) { + return mDemux->startRecordFilterDispatcher(); + } else { + return mDemux->startBroadcastFilterDispatcher(); + } + } + + map>::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; +} + +bool Dvr::writeRecordFMQ(const vector& data) { + lock_guard lock(mWriteLock); + if (mRecordStatus == RecordStatus::OVERFLOW) { + ALOGW("[Dvr] stops writing and wait for the client side flushing."); + return true; + } + if (mDvrMQ->write(data.data(), data.size())) { + mDvrEventFlag->wake(static_cast(DemuxQueueNotifyBits::DATA_READY)); + maySendRecordStatusCallback(); + return true; + } + + maySendRecordStatusCallback(); + return false; +} + +void Dvr::maySendRecordStatusCallback() { + lock_guard lock(mRecordStatusLock); + int availableToRead = mDvrMQ->availableToRead(); + int availableToWrite = mDvrMQ->availableToWrite(); + + RecordStatus newStatus = checkRecordStatusChange(availableToWrite, availableToRead, + mDvrSettings.record().highThreshold, + mDvrSettings.record().lowThreshold); + if (mRecordStatus != newStatus) { + mCallback->onRecordStatus(newStatus); + mRecordStatus = newStatus; + } +} + +RecordStatus Dvr::checkRecordStatusChange(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 mRecordStatus; +} + +bool Dvr::addPlaybackFilter(uint64_t filterId, sp filter) { + mFilters[filterId] = filter; + return true; +} + +bool Dvr::removePlaybackFilter(uint64_t filterId) { + mFilters.erase(filterId); + return true; +} +} // namespace implementation +} // namespace V1_0 +} // namespace tuner +} // namespace tv +} // namespace hardware +} // namespace android diff --git a/tv/tuner/1.1/default/Dvr.h b/tv/tuner/1.1/default/Dvr.h new file mode 100644 index 0000000000..7b7efefada --- /dev/null +++ b/tv/tuner/1.1/default/Dvr.h @@ -0,0 +1,167 @@ +/* + * Copyright 2020 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_1_DVR_H_ +#define ANDROID_HARDWARE_TV_TUNER_V1_1_DVR_H_ + +#include +#include +#include +#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 DvrMQ = MessageQueue; + +struct MediaEsMetaData { + bool isAudio; + int startIndex; + int len; + int pts; +}; + +class Demux; +class Filter; +class Frontend; +class Tuner; + +class Dvr : public IDvr { + public: + Dvr(); + + Dvr(DvrType type, uint32_t bufferSize, const sp& cb, sp demux); + + ~Dvr(); + + virtual Return getQueueDesc(getQueueDesc_cb _hidl_cb) override; + + virtual Return configure(const DvrSettings& settings) override; + + virtual Return attachFilter(const sp& filter) override; + + virtual Return detachFilter(const sp& filter) override; + + virtual Return start() override; + + virtual Return stop() override; + + virtual Return flush() override; + + virtual Return close() override; + + /** + * To create a DvrMQ and its Event Flag. + * + * Return false is any of the above processes fails. + */ + bool createDvrMQ(); + void sendBroadcastInputToDvrRecord(vector byteBuffer); + bool writeRecordFMQ(const std::vector& data); + bool addPlaybackFilter(uint64_t filterId, sp filter); + bool removePlaybackFilter(uint64_t filterId); + bool readPlaybackFMQ(bool isVirtualFrontend, bool isRecording); + bool processEsDataOnPlayback(bool isVirtualFrontend, bool isRecording); + bool startFilterDispatcher(bool isVirtualFrontend, bool isRecording); + EventFlag* getDvrEventFlag(); + DvrSettings getSettings() { return mDvrSettings; } + + private: + // Demux service + sp mDemux; + + DvrType mType; + uint32_t mBufferSize; + sp mCallback; + std::map> mFilters; + + void deleteEventFlag(); + bool readDataFromMQ(); + void getMetaDataValue(int& index, uint8_t* dataOutputBuffer, int& value); + void maySendPlaybackStatusCallback(); + void maySendRecordStatusCallback(); + PlaybackStatus checkPlaybackStatusChange(uint32_t availableToWrite, uint32_t availableToRead, + uint32_t highThreshold, uint32_t lowThreshold); + RecordStatus checkRecordStatusChange(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 startTpidFilter(vector data); + static void* __threadLoopPlayback(void* user); + static void* __threadLoopRecord(void* user); + void playbackThreadLoop(); + void recordThreadLoop(); + + unique_ptr mDvrMQ; + EventFlag* mDvrEventFlag; + /** + * Demux callbacks used on filter events or IO buffer status + */ + bool mDvrConfigured = false; + DvrSettings mDvrSettings; + + // Thread handlers + pthread_t mDvrThread; + + // FMQ status local records + PlaybackStatus mPlaybackStatus; + RecordStatus mRecordStatus; + /** + * If a specific filter's writing loop is still running + */ + bool mDvrThreadRunning; + 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 mRecordStatusLock; + std::mutex mDvrThreadLock; + + const bool DEBUG_DVR = false; + + // Booleans to check if recording is running. + // Recording is ready when both of the following are set to true. + bool mIsRecordStarted = false; +}; + +} // namespace implementation +} // namespace V1_0 +} // namespace tuner +} // namespace tv +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_TV_TUNER_V1_1_DVR_H_ \ No newline at end of file diff --git a/tv/tuner/1.1/default/Filter.cpp b/tv/tuner/1.1/default/Filter.cpp new file mode 100644 index 0000000000..108baf74f3 --- /dev/null +++ b/tv/tuner/1.1/default/Filter.cpp @@ -0,0 +1,672 @@ +/* + * Copyright 2020 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.1-Filter" + +#include "Filter.h" +#include + +namespace android { +namespace hardware { +namespace tv { +namespace tuner { +namespace V1_0 { +namespace implementation { + +#define WAIT_TIMEOUT 3000000000 + +Filter::Filter() {} + +Filter::Filter(DemuxFilterType type, uint64_t filterId, uint32_t bufferSize, + const sp& cb, sp demux) { + mType = type; + mFilterId = filterId; + mBufferSize = bufferSize; + mCallback = cb; + mDemux = demux; + + switch (mType.mainType) { + case DemuxFilterMainType::TS: + if (mType.subType.tsFilterType() == DemuxTsFilterType::AUDIO || + mType.subType.tsFilterType() == DemuxTsFilterType::VIDEO) { + mIsMediaFilter = true; + } + if (mType.subType.tsFilterType() == DemuxTsFilterType::PCR) { + mIsPcrFilter = true; + } + if (mType.subType.tsFilterType() == DemuxTsFilterType::RECORD) { + mIsRecordFilter = true; + } + break; + case DemuxFilterMainType::MMTP: + if (mType.subType.mmtpFilterType() == DemuxMmtpFilterType::AUDIO || + mType.subType.mmtpFilterType() == DemuxMmtpFilterType::VIDEO) { + mIsMediaFilter = true; + } + if (mType.subType.mmtpFilterType() == DemuxMmtpFilterType::RECORD) { + mIsRecordFilter = true; + } + break; + case DemuxFilterMainType::IP: + break; + case DemuxFilterMainType::TLV: + break; + case DemuxFilterMainType::ALP: + break; + default: + break; + } +} + +Filter::~Filter() {} + +Return Filter::getId64Bit(getId64Bit_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + _hidl_cb(Result::SUCCESS, mFilterId); + return Void(); +} + +Return Filter::getId(getId_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + _hidl_cb(Result::SUCCESS, static_cast(mFilterId)); + return Void(); +} + +Return Filter::setDataSource(const sp& filter) { + ALOGV("%s", __FUNCTION__); + + mDataSource = filter; + mIsDataSourceDemux = false; + + return Result::SUCCESS; +} + +Return Filter::getQueueDesc(getQueueDesc_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + mIsUsingFMQ = true; + + _hidl_cb(Result::SUCCESS, *mFilterMQ->getDesc()); + return Void(); +} + +Return Filter::configure(const DemuxFilterSettings& settings) { + ALOGV("%s", __FUNCTION__); + + mFilterSettings = settings; + switch (mType.mainType) { + case DemuxFilterMainType::TS: + mTpid = settings.ts().tpid; + break; + case DemuxFilterMainType::MMTP: + break; + case DemuxFilterMainType::IP: + break; + case DemuxFilterMainType::TLV: + break; + case DemuxFilterMainType::ALP: + break; + default: + break; + } + + return Result::SUCCESS; +} + +Return Filter::start() { + ALOGV("%s", __FUNCTION__); + + return startFilterLoop(); +} + +Return Filter::stop() { + ALOGV("%s", __FUNCTION__); + + mFilterThreadRunning = false; + + std::lock_guard lock(mFilterThreadLock); + + return Result::SUCCESS; +} + +Return 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 Filter::releaseAvHandle(const hidl_handle& /*avMemory*/, uint64_t avDataId) { + ALOGV("%s", __FUNCTION__); + if (mDataId2Avfd.find(avDataId) == mDataId2Avfd.end()) { + return Result::INVALID_ARGUMENT; + } + + ::close(mDataId2Avfd[avDataId]); + return Result::SUCCESS; +} + +Return 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 tmpFilterMQ = + std::unique_ptr(new (std::nothrow) FilterMQ(mBufferSize, true)); + if (!tmpFilterMQ->isValid()) { + ALOGW("[Filter] Failed to create FMQ of filter with id: %" PRIu64, 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(user); + self->filterThreadLoop(); + return 0; +} + +void Filter::filterThreadLoop() { + ALOGD("[Filter] filter %" PRIu64 " threadLoop start.", mFilterId); + std::lock_guard 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) { + if (DEBUG_FILTER) { + 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); + freeAvHandle(); + mFilterEvent.events.resize(0); + mFilterStatus = DemuxFilterStatus::DATA_READY; + if (mCallback == nullptr) { + ALOGD("[Filter] filter %" PRIu64 " does not hava callback. Ending thread", mFilterId); + break; + } + 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 && mIsUsingFMQ) { + status_t status = mFilterEventFlag->wait( + static_cast(DemuxQueueNotifyBits::DATA_CONSUMED), &efState, + WAIT_TIMEOUT, true /* retry on spurious wake */); + if (status != OK) { + ALOGD("[Filter] wait for data consumed"); + continue; + } + break; + } + + maySendFilterStatusCallback(); + + while (mFilterThreadRunning) { + std::lock_guard 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 %" PRIu64 " writing done. Ending thread", mFilterId); + break; + } + } + mFilterThreadRunning = false; + } + + ALOGD("[Filter] filter thread ended."); +} + +void Filter::freeAvHandle() { + if (!mIsMediaFilter) { + return; + } + for (int i = 0; i < mFilterEvent.events.size(); i++) { + ::close(mFilterEvent.events[i].media().avMemory.getNativeHandle()->data[0]); + native_handle_close(mFilterEvent.events[i].media().avMemory.getNativeHandle()); + } +} + +void Filter::maySendFilterStatusCallback() { + if (!mIsUsingFMQ) { + return; + } + std::lock_guard 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 data) { + std::lock_guard lock(mFilterOutputLock); + mFilterOutput.insert(mFilterOutput.end(), data.begin(), data.end()); +} + +void Filter::updatePts(uint64_t pts) { + std::lock_guard lock(mFilterOutputLock); + mPts = pts; +} + +void Filter::updateRecordOutput(vector data) { + std::lock_guard lock(mRecordFilterOutputLock); + mRecordFilterOutput.insert(mRecordFilterOutput.end(), data.begin(), data.end()); +} + +Result Filter::startFilterHandler() { + std::lock_guard 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::TEMI: + startTemiFilterHandler(); + break; + default: + 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 %" PRIu64 " fails to write into FMQ. Ending thread", mFilterId); + return Result::UNKNOWN_ERROR; + } + + mFilterOutput.clear(); + + return Result::SUCCESS; +} + +Result Filter::startPesFilterHandler() { + std::lock_guard 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]; + if (DEBUG_FILTER) { + ALOGD("[Filter] prefix %d", prefix); + } + if (prefix == 0x000001) { + // TODO handle mulptiple Pes filters + mPesSizeLeft = (mFilterOutput[i + 8] << 8) | mFilterOutput[i + 9]; + mPesSizeLeft += 6; + if (DEBUG_FILTER) { + ALOGD("[Filter] pes data length %d", mPesSizeLeft); + } + } else { + continue; + } + } + + int endPoint = min(184, mPesSizeLeft); + // append data and check size + vector::const_iterator first = mFilterOutput.begin() + i + 4; + vector::const_iterator last = mFilterOutput.begin() + i + 4 + endPoint; + mPesOutput.insert(mPesOutput.end(), first, last); + // size does not match then continue + mPesSizeLeft -= endPoint; + if (DEBUG_FILTER) { + 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(mPesOutput.size()), + }; + if (DEBUG_FILTER) { + 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() { + std::lock_guard lock(mFilterEventLock); + if (mFilterOutput.empty()) { + return Result::SUCCESS; + } + + if (mPts) { + return createMediaFilterEventWithIon(mFilterOutput); + } + + 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]; + if (DEBUG_FILTER) { + ALOGD("[Filter] prefix %d", prefix); + } + if (prefix == 0x000001) { + // TODO handle mulptiple Pes filters + mPesSizeLeft = (mFilterOutput[i + 8] << 8) | mFilterOutput[i + 9]; + mPesSizeLeft += 6; + if (DEBUG_FILTER) { + ALOGD("[Filter] pes data length %d", mPesSizeLeft); + } + } else { + continue; + } + } + + int endPoint = min(184, mPesSizeLeft); + // append data and check size + vector::const_iterator first = mFilterOutput.begin() + i + 4; + vector::const_iterator last = mFilterOutput.begin() + i + 4 + endPoint; + mPesOutput.insert(mPesOutput.end(), first, last); + // size does not match then continue + mPesSizeLeft -= endPoint; + if (DEBUG_FILTER) { + ALOGD("[Filter] pes data left %d", mPesSizeLeft); + } + if (mPesSizeLeft > 0 || mAvBufferCopyCount++ < 10) { + continue; + } + + createMediaFilterEventWithIon(mPesOutput); + } + + mFilterOutput.clear(); + + return Result::SUCCESS; +} + +Result Filter::createMediaFilterEventWithIon(vector output) { + int av_fd = createAvIonFd(output.size()); + if (av_fd == -1) { + return Result::UNKNOWN_ERROR; + } + // copy the filtered data to the buffer + uint8_t* avBuffer = getIonBuffer(av_fd, output.size()); + if (avBuffer == NULL) { + return Result::UNKNOWN_ERROR; + } + memcpy(avBuffer, output.data(), output.size() * sizeof(uint8_t)); + + native_handle_t* nativeHandle = createNativeHandle(av_fd); + if (nativeHandle == NULL) { + return Result::UNKNOWN_ERROR; + } + hidl_handle handle; + handle.setTo(nativeHandle, /*shouldOwn=*/true); + + // Create a dataId and add a pair into the dataId2Avfd map + uint64_t dataId = mLastUsedDataId++ /*createdUID*/; + mDataId2Avfd[dataId] = dup(av_fd); + + // Create mediaEvent and send callback + DemuxFilterMediaEvent mediaEvent; + mediaEvent = { + .avMemory = std::move(handle), + .dataLength = static_cast(output.size()), + .avDataId = dataId, + }; + if (mPts) { + mediaEvent.pts = mPts; + mPts = 0; + } + int size = mFilterEvent.events.size(); + mFilterEvent.events.resize(size + 1); + mFilterEvent.events[size].media(mediaEvent); + + // Clear and log + output.clear(); + mAvBufferCopyCount = 0; + ::close(av_fd); + if (DEBUG_FILTER) { + ALOGD("[Filter] av data length %d", mediaEvent.dataLength); + } + return Result::SUCCESS; +} + +Result Filter::startRecordFilterHandler() { + std::lock_guard lock(mRecordFilterOutputLock); + if (mRecordFilterOutput.empty()) { + return Result::SUCCESS; + } + + if (mDvr == nullptr || !mDvr->writeRecordFMQ(mRecordFilterOutput)) { + ALOGD("[Filter] dvr fails to write into record FMQ."); + return Result::UNKNOWN_ERROR; + } + + mRecordFilterOutput.clear(); + return Result::SUCCESS; +} + +Result Filter::startPcrFilterHandler() { + // TODO handle starting PCR filter + return Result::SUCCESS; +} + +Result Filter::startTemiFilterHandler() { + // TODO handle starting TEMI filter + return Result::SUCCESS; +} + +bool Filter::writeSectionsAndCreateEvent(vector data) { + // TODO check how many sections has been read + ALOGD("[Filter] section handler"); + std::lock_guard 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(data.size()), + }; + mFilterEvent.events[size].section(secEvent); + return true; +} + +bool Filter::writeDataToFilterMQ(const std::vector& data) { + std::lock_guard lock(mWriteLock); + if (mFilterMQ->write(data.data(), data.size())) { + return true; + } + return false; +} + +void Filter::attachFilterToRecord(const sp dvr) { + mDvr = dvr; +} + +void Filter::detachFilterFromRecord() { + mDvr = nullptr; +} + +int Filter::createAvIonFd(int size) { + // Create an ion fd and allocate an av fd mapped to a buffer to it. + int ion_fd = ion_open(); + if (ion_fd == -1) { + ALOGE("[Filter] Failed to open ion fd %d", errno); + return -1; + } + int av_fd = -1; + ion_alloc_fd(dup(ion_fd), size, 0 /*align*/, ION_HEAP_SYSTEM_MASK, 0 /*flags*/, &av_fd); + if (av_fd == -1) { + ALOGE("[Filter] Failed to create av fd %d", errno); + return -1; + } + return av_fd; +} + +uint8_t* Filter::getIonBuffer(int fd, int size) { + uint8_t* avBuf = static_cast( + mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 /*offset*/)); + if (avBuf == MAP_FAILED) { + ALOGE("[Filter] fail to allocate buffer %d", errno); + return NULL; + } + return avBuf; +} + +native_handle_t* Filter::createNativeHandle(int fd) { + // Create a native handle to pass the av fd via the callback event. + native_handle_t* nativeHandle = native_handle_create(/*numFd*/ 1, 0); + if (nativeHandle == NULL) { + ALOGE("[Filter] Failed to create native_handle %d", errno); + return NULL; + } + nativeHandle->data[0] = dup(fd); + return nativeHandle; +} +} // namespace implementation +} // namespace V1_0 +} // namespace tuner +} // namespace tv +} // namespace hardware +} // namespace android diff --git a/tv/tuner/1.1/default/Filter.h b/tv/tuner/1.1/default/Filter.h new file mode 100644 index 0000000000..d5801d4f74 --- /dev/null +++ b/tv/tuner/1.1/default/Filter.h @@ -0,0 +1,214 @@ +/* + * Copyright 2020 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_1_FILTER_H_ +#define ANDROID_HARDWARE_TV_TUNER_V1_1_FILTER_H_ + +#include +#include +#include +#include +#include +#include +#include "Demux.h" +#include "Dvr.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 FilterMQ = MessageQueue; + +class Demux; +class Dvr; + +class Filter : public V1_1::IFilter { + public: + Filter(); + + Filter(DemuxFilterType type, uint64_t filterId, uint32_t bufferSize, + const sp& cb, sp demux); + + ~Filter(); + + virtual Return getId64Bit(getId64Bit_cb _hidl_cb) override; + + virtual Return getId(getId_cb _hidl_cb) override; + + virtual Return setDataSource(const sp& filter) override; + + virtual Return getQueueDesc(getQueueDesc_cb _hidl_cb) override; + + virtual Return configure(const DemuxFilterSettings& settings) override; + + virtual Return start() override; + + virtual Return stop() override; + + virtual Return flush() override; + + virtual Return releaseAvHandle(const hidl_handle& avMemory, uint64_t avDataId) override; + + virtual Return 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 data); + void updateRecordOutput(vector data); + void updatePts(uint64_t pts); + Result startFilterHandler(); + Result startRecordFilterHandler(); + void attachFilterToRecord(const sp dvr); + void detachFilterFromRecord(); + void freeAvHandle(); + bool isMediaFilter() { return mIsMediaFilter; }; + bool isPcrFilter() { return mIsPcrFilter; }; + bool isRecordFilter() { return mIsRecordFilter; }; + + private: + // Tuner service + sp mDemux; + // Dvr reference once the filter is attached to any + sp mDvr = nullptr; + /** + * Filter callbacks used on filter events or FMQ status + */ + sp mCallback; + + uint64_t mFilterId; + uint32_t mBufferSize; + DemuxFilterType mType; + bool mIsMediaFilter = false; + bool mIsPcrFilter = false; + bool mIsRecordFilter = false; + DemuxFilterSettings mFilterSettings; + + uint16_t mTpid; + sp mDataSource; + bool mIsDataSourceDemux = true; + vector mFilterOutput; + vector mRecordFilterOutput; + uint64_t mPts = 0; + unique_ptr mFilterMQ; + bool mIsUsingFMQ = false; + 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; + + bool DEBUG_FILTER = false; + + /** + * 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 startPcrFilterHandler(); + Result startTemiFilterHandler(); + Result startFilterLoop(); + + void deleteEventFlag(); + bool writeDataToFilterMQ(const std::vector& data); + bool readDataFromMQ(); + bool writeSectionsAndCreateEvent(vector 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 data); + bool startFilterDispatcher(); + static void* __threadLoopFilter(void* user); + void filterThreadLoop(); + + int createAvIonFd(int size); + uint8_t* getIonBuffer(int fd, int size); + native_handle_t* createNativeHandle(int fd); + Result createMediaFilterEventWithIon(vector output); + + /** + * 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; + std::mutex mRecordFilterOutputLock; + + // temp handle single PES filter + // TODO handle mulptiple Pes filters + int mPesSizeLeft = 0; + vector mPesOutput; + + // A map from data id to ion handle + std::map mDataId2Avfd; + uint64_t mLastUsedDataId = 1; + int mAvBufferCopyCount = 0; +}; + +} // namespace implementation +} // namespace V1_0 +} // namespace tuner +} // namespace tv +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_TV_TUNER_V1_1_FILTER_H_ diff --git a/tv/tuner/1.1/default/Frontend.cpp b/tv/tuner/1.1/default/Frontend.cpp new file mode 100644 index 0000000000..3475c3686b --- /dev/null +++ b/tv/tuner/1.1/default/Frontend.cpp @@ -0,0 +1,284 @@ +/* + * Copyright 2020 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.1-Frontend" + +#include "Frontend.h" +#include +#include + +namespace android { +namespace hardware { +namespace tv { +namespace tuner { +namespace V1_0 { +namespace implementation { + +Frontend::Frontend(FrontendType type, FrontendId id, sp tuner) { + mType = type; + mId = id; + mTunerService = tuner; + // Init callback to nullptr + mCallback = nullptr; +} + +Frontend::~Frontend() {} + +Return Frontend::close() { + ALOGV("%s", __FUNCTION__); + // Reset callback + mCallback = nullptr; + mIsLocked = false; + + return Result::SUCCESS; +} + +Return Frontend::setCallback(const sp& callback) { + ALOGV("%s", __FUNCTION__); + if (callback == nullptr) { + ALOGW("[ WARN ] Set Frontend callback with nullptr"); + return Result::INVALID_ARGUMENT; + } + + mCallback = callback; + return Result::SUCCESS; +} + +Return Frontend::tune(const FrontendSettings& /* settings */) { + ALOGV("%s", __FUNCTION__); + if (mCallback == nullptr) { + ALOGW("[ WARN ] Frontend callback is not set when tune"); + return Result::INVALID_STATE; + } + + mTunerService->frontendStartTune(mId); + mCallback->onEvent(FrontendEventType::LOCKED); + mIsLocked = true; + return Result::SUCCESS; +} + +Return Frontend::stopTune() { + ALOGV("%s", __FUNCTION__); + + mTunerService->frontendStopTune(mId); + mIsLocked = false; + + return Result::SUCCESS; +} + +Return Frontend::scan(const FrontendSettings& settings, FrontendScanType type) { + ALOGV("%s", __FUNCTION__); + + if (mType == FrontendType::ATSC) { + FrontendScanMessage msg; + msg.isLocked(true); + mCallback->onScanMessage(FrontendScanMessageType::LOCKED, msg); + mIsLocked = true; + return Result::SUCCESS; + } + if (mType != FrontendType::DVBT) { + return Result::UNAVAILABLE; + } + + FrontendScanMessage msg; + + if (mIsLocked) { + msg.isEnd(true); + mCallback->onScanMessage(FrontendScanMessageType::END, msg); + return Result::SUCCESS; + } + + uint32_t frequency = settings.dvbt().frequency; + if (type == FrontendScanType::SCAN_BLIND) { + frequency += 100; + } + msg.frequencies({frequency}); + mCallback->onScanMessage(FrontendScanMessageType::FREQUENCY, msg); + msg.isLocked(true); + mCallback->onScanMessage(FrontendScanMessageType::LOCKED, msg); + mIsLocked = true; + + return Result::SUCCESS; +} + +Return Frontend::stopScan() { + ALOGV("%s", __FUNCTION__); + + mIsLocked = false; + return Result::SUCCESS; +} + +Return Frontend::getStatus(const hidl_vec& statusTypes, + getStatus_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + vector statuses; + for (int i = 0; i < statusTypes.size(); i++) { + FrontendStatusType type = statusTypes[i]; + FrontendStatus status; + // assign randomly selected values for testing. + switch (type) { + case FrontendStatusType::DEMOD_LOCK: { + status.isDemodLocked(true); + break; + } + case FrontendStatusType::SNR: { + status.snr(221); + break; + } + case FrontendStatusType::BER: { + status.ber(1); + break; + } + case FrontendStatusType::PER: { + status.per(2); + break; + } + case FrontendStatusType::PRE_BER: { + status.preBer(3); + break; + } + case FrontendStatusType::SIGNAL_QUALITY: { + status.signalQuality(4); + break; + } + case FrontendStatusType::SIGNAL_STRENGTH: { + status.signalStrength(5); + break; + } + case FrontendStatusType::SYMBOL_RATE: { + status.symbolRate(6); + break; + } + case FrontendStatusType::FEC: { + status.innerFec(FrontendInnerFec::FEC_2_9); // value = 1 << 7 + break; + } + case FrontendStatusType::MODULATION: { + FrontendModulationStatus modulationStatus; + modulationStatus.isdbt(FrontendIsdbtModulation::MOD_16QAM); // value = 1 << 3 + status.modulation(modulationStatus); + break; + } + case FrontendStatusType::SPECTRAL: { + status.inversion(FrontendDvbcSpectralInversion::NORMAL); + break; + } + case FrontendStatusType::LNB_VOLTAGE: { + status.lnbVoltage(LnbVoltage::VOLTAGE_5V); + break; + } + case FrontendStatusType::PLP_ID: { + status.plpId(101); // type uint8_t + break; + } + case FrontendStatusType::EWBS: { + status.isEWBS(false); + break; + } + case FrontendStatusType::AGC: { + status.agc(7); + break; + } + case FrontendStatusType::LNA: { + status.isLnaOn(false); + break; + } + case FrontendStatusType::LAYER_ERROR: { + vector v = {false, true, true}; + status.isLayerError(v); + break; + } + case FrontendStatusType::MER: { + status.mer(8); + break; + } + case FrontendStatusType::FREQ_OFFSET: { + status.freqOffset(9); + break; + } + case FrontendStatusType::HIERARCHY: { + status.hierarchy(FrontendDvbtHierarchy::HIERARCHY_1_NATIVE); + break; + } + case FrontendStatusType::RF_LOCK: { + status.isRfLocked(false); + break; + } + case FrontendStatusType::ATSC3_PLP_INFO: { + vector v; + FrontendStatusAtsc3PlpInfo info1{ + .plpId = 3, + .isLocked = false, + .uec = 313, + }; + FrontendStatusAtsc3PlpInfo info2{ + .plpId = 5, + .isLocked = true, + .uec = 515, + }; + v.push_back(info1); + v.push_back(info2); + status.plpInfo(v); + break; + } + default: { + continue; + } + } + statuses.push_back(status); + } + _hidl_cb(Result::SUCCESS, statuses); + + return Void(); +} + +Return Frontend::setLna(bool /* bEnable */) { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +Return Frontend::setLnb(uint32_t /* lnb */) { + ALOGV("%s", __FUNCTION__); + if (!supportsSatellite()) { + return Result::INVALID_STATE; + } + return Result::SUCCESS; +} + +FrontendType Frontend::getFrontendType() { + return mType; +} + +FrontendId Frontend::getFrontendId() { + return mId; +} + +bool Frontend::supportsSatellite() { + return mType == FrontendType::DVBS || mType == FrontendType::ISDBS || + mType == FrontendType::ISDBS3; +} + +bool Frontend::isLocked() { + return mIsLocked; +} +} // namespace implementation +} // namespace V1_0 +} // namespace tuner +} // namespace tv +} // namespace hardware +} // namespace android diff --git a/tv/tuner/1.1/default/Frontend.h b/tv/tuner/1.1/default/Frontend.h new file mode 100644 index 0000000000..89b4a6b5f3 --- /dev/null +++ b/tv/tuner/1.1/default/Frontend.h @@ -0,0 +1,85 @@ +/* + * Copyright 2020 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_1_FRONTEND_H_ +#define ANDROID_HARDWARE_TV_TUNER_V1_1_FRONTEND_H_ + +#include +#include +#include "Tuner.h" + +using namespace std; + +namespace android { +namespace hardware { +namespace tv { +namespace tuner { +namespace V1_0 { +namespace implementation { + +class Tuner; + +class Frontend : public IFrontend { + public: + Frontend(FrontendType type, FrontendId id, sp tuner); + + virtual Return close() override; + + virtual Return setCallback(const sp& callback) override; + + virtual Return tune(const FrontendSettings& settings) override; + + virtual Return stopTune() override; + + virtual Return scan(const FrontendSettings& settings, FrontendScanType type) override; + + virtual Return stopScan() override; + + virtual Return getStatus(const hidl_vec& statusTypes, + getStatus_cb _hidl_cb) override; + + virtual Return setLna(bool bEnable) override; + + virtual Return setLnb(uint32_t lnb) override; + + FrontendType getFrontendType(); + + FrontendId getFrontendId(); + + string getSourceFile(); + + bool isLocked(); + + private: + virtual ~Frontend(); + bool supportsSatellite(); + sp mCallback; + sp mTunerService; + FrontendType mType = FrontendType::UNDEFINED; + FrontendId mId = 0; + bool mIsLocked = false; + + std::ifstream mFrontendData; +}; + +} // namespace implementation +} // namespace V1_0 +} // namespace tuner +} // namespace tv +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_TV_TUNER_V1_1_FRONTEND_H_ diff --git a/tv/tuner/1.1/default/Lnb.cpp b/tv/tuner/1.1/default/Lnb.cpp new file mode 100644 index 0000000000..044727ff54 --- /dev/null +++ b/tv/tuner/1.1/default/Lnb.cpp @@ -0,0 +1,81 @@ +/* + * Copyright 2020 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.1-Lnb" + +#include "Lnb.h" +#include + +namespace android { +namespace hardware { +namespace tv { +namespace tuner { +namespace V1_0 { +namespace implementation { + +Lnb::Lnb() {} +Lnb::Lnb(int id) { + mId = id; +} + +Lnb::~Lnb() {} + +Return Lnb::setCallback(const sp& /* callback */) { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +Return Lnb::setVoltage(LnbVoltage /* voltage */) { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +Return Lnb::setTone(LnbTone /* tone */) { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +Return Lnb::setSatellitePosition(LnbPosition /* position */) { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +Return Lnb::sendDiseqcMessage(const hidl_vec& /* diseqcMessage */) { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +Return Lnb::close() { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +int Lnb::getId() { + return mId; +} + +} // namespace implementation +} // namespace V1_0 +} // namespace tuner +} // namespace tv +} // namespace hardware +} // namespace android diff --git a/tv/tuner/1.1/default/Lnb.h b/tv/tuner/1.1/default/Lnb.h new file mode 100644 index 0000000000..70a8e41b8b --- /dev/null +++ b/tv/tuner/1.1/default/Lnb.h @@ -0,0 +1,63 @@ +/* + * Copyright 2020 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_1_LNB_H_ +#define ANDROID_HARDWARE_TV_TUNER_V1_1_LNB_H_ + +#include +#include + +using namespace std; + +namespace android { +namespace hardware { +namespace tv { +namespace tuner { +namespace V1_0 { +namespace implementation { + +class Lnb : public ILnb { + public: + Lnb(); + Lnb(int id); + + virtual Return setCallback(const sp& callback) override; + + virtual Return setVoltage(LnbVoltage voltage) override; + + virtual Return setTone(LnbTone tone) override; + + virtual Return setSatellitePosition(LnbPosition position) override; + + virtual Return sendDiseqcMessage(const hidl_vec& diseqcMessage) override; + + virtual Return close() override; + + int getId(); + + private: + int mId; + virtual ~Lnb(); +}; + +} // namespace implementation +} // namespace V1_0 +} // namespace tuner +} // namespace tv +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_TV_TUNER_V1_1_LNB_H_ diff --git a/tv/tuner/1.1/default/OWNERS b/tv/tuner/1.1/default/OWNERS new file mode 100644 index 0000000000..1b3d095f9c --- /dev/null +++ b/tv/tuner/1.1/default/OWNERS @@ -0,0 +1,4 @@ +nchalko@google.com +amyjojo@google.com +shubang@google.com +quxiangfang@google.com diff --git a/tv/tuner/1.1/default/TimeFilter.cpp b/tv/tuner/1.1/default/TimeFilter.cpp new file mode 100644 index 0000000000..bb243a66bc --- /dev/null +++ b/tv/tuner/1.1/default/TimeFilter.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2020 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.1-TimeFilter" + +#include "TimeFilter.h" +#include + +namespace android { +namespace hardware { +namespace tv { +namespace tuner { +namespace V1_0 { +namespace implementation { + +TimeFilter::TimeFilter() {} + +TimeFilter::TimeFilter(sp demux) { + mDemux = demux; +} + +TimeFilter::~TimeFilter() {} + +Return TimeFilter::setTimeStamp(uint64_t timeStamp) { + ALOGV("%s", __FUNCTION__); + if (timeStamp == INVALID_TIME_STAMP) { + return Result::INVALID_ARGUMENT; + } + mTimeStamp = timeStamp; + mBeginTime = time(NULL); + + return Result::SUCCESS; +} + +Return TimeFilter::clearTimeStamp() { + ALOGV("%s", __FUNCTION__); + mTimeStamp = INVALID_TIME_STAMP; + + return Result::SUCCESS; +} + +Return TimeFilter::getTimeStamp(getTimeStamp_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + if (mTimeStamp == INVALID_TIME_STAMP) { + _hidl_cb(Result::INVALID_STATE, mTimeStamp); + } + + uint64_t currentTimeStamp = mTimeStamp + difftime(time(NULL), mBeginTime) * 900000; + _hidl_cb(Result::SUCCESS, currentTimeStamp); + return Void(); +} + +Return TimeFilter::getSourceTime(getSourceTime_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + uint64_t time = 0; + + _hidl_cb(Result::SUCCESS, time); + return Void(); +} + +Return TimeFilter::close() { + ALOGV("%s", __FUNCTION__); + mTimeStamp = INVALID_TIME_STAMP; + + 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.1/default/TimeFilter.h b/tv/tuner/1.1/default/TimeFilter.h new file mode 100644 index 0000000000..d53ad2c900 --- /dev/null +++ b/tv/tuner/1.1/default/TimeFilter.h @@ -0,0 +1,70 @@ +/* + * Copyright 2020 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_1_TIMEFILTER_H_ +#define ANDROID_HARDWARE_TV_TUNER_V1_1_TIMEFILTER_H_ + +#include +#include "Demux.h" +#include "time.h" + +using namespace std; + +namespace android { +namespace hardware { +namespace tv { +namespace tuner { +namespace V1_0 { +namespace implementation { + +using FilterMQ = MessageQueue; + +#define INVALID_TIME_STAMP -1 + +class Demux; + +class TimeFilter : public ITimeFilter { + public: + TimeFilter(); + + TimeFilter(sp demux); + + ~TimeFilter(); + + virtual Return setTimeStamp(uint64_t timeStamp) override; + + virtual Return clearTimeStamp() override; + + virtual Return getTimeStamp(getTimeStamp_cb _hidl_cb) override; + + virtual Return getSourceTime(getSourceTime_cb _hidl_cb) override; + + virtual Return close() override; + + private: + sp mDemux; + uint64_t mTimeStamp = INVALID_TIME_STAMP; + time_t mBeginTime; +}; + +} // namespace implementation +} // namespace V1_0 +} // namespace tuner +} // namespace tv +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_TV_TUNER_V1_1_TIMEFILTER_H_ \ No newline at end of file diff --git a/tv/tuner/1.1/default/Tuner.cpp b/tv/tuner/1.1/default/Tuner.cpp new file mode 100644 index 0000000000..c220ea2682 --- /dev/null +++ b/tv/tuner/1.1/default/Tuner.cpp @@ -0,0 +1,259 @@ +/* + * Copyright 2020 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.1-Tuner" + +#include "Tuner.h" +#include +#include "Demux.h" +#include "Descrambler.h" +#include "Frontend.h" +#include "Lnb.h" + +namespace android { +namespace hardware { +namespace tv { +namespace tuner { +namespace V1_0 { +namespace implementation { + +Tuner::Tuner() { + // Static Frontends array to maintain local frontends information + // Array index matches their FrontendId in the default impl + mFrontendSize = 8; + mFrontends.resize(mFrontendSize); + mFrontends[0] = new Frontend(FrontendType::DVBT, 0, this); + mFrontends[1] = new Frontend(FrontendType::ATSC, 1, this); + mFrontends[2] = new Frontend(FrontendType::DVBC, 2, this); + mFrontends[3] = new Frontend(FrontendType::DVBS, 3, this); + mFrontends[4] = new Frontend(FrontendType::DVBT, 4, this); + mFrontends[5] = new Frontend(FrontendType::ISDBT, 5, this); + mFrontends[6] = new Frontend(FrontendType::ANALOG, 6, this); + mFrontends[7] = new Frontend(FrontendType::ATSC, 7, this); + + FrontendInfo::FrontendCapabilities caps; + mFrontendCaps.resize(mFrontendSize); + caps = FrontendInfo::FrontendCapabilities(); + caps.dvbtCaps(FrontendDvbtCapabilities()); + mFrontendCaps[0] = caps; + + caps = FrontendInfo::FrontendCapabilities(); + caps.atscCaps(FrontendAtscCapabilities()); + mFrontendCaps[1] = caps; + + caps = FrontendInfo::FrontendCapabilities(); + caps.dvbcCaps(FrontendDvbcCapabilities()); + mFrontendCaps[2] = caps; + + caps = FrontendInfo::FrontendCapabilities(); + caps.dvbsCaps(FrontendDvbsCapabilities()); + mFrontendCaps[3] = caps; + + caps = FrontendInfo::FrontendCapabilities(); + caps.dvbtCaps(FrontendDvbtCapabilities()); + mFrontendCaps[4] = caps; + + caps = FrontendInfo::FrontendCapabilities(); + FrontendIsdbtCapabilities isdbtCaps{ + .modeCap = FrontendIsdbtMode::MODE_1 | FrontendIsdbtMode::MODE_2, + .bandwidthCap = (unsigned int)FrontendIsdbtBandwidth::BANDWIDTH_6MHZ, + .modulationCap = (unsigned int)FrontendIsdbtModulation::MOD_16QAM, + // ISDBT shares coderate and guard interval with DVBT + .coderateCap = FrontendDvbtCoderate::CODERATE_4_5 | FrontendDvbtCoderate::CODERATE_6_7, + .guardIntervalCap = (unsigned int)FrontendDvbtGuardInterval::INTERVAL_1_128, + }; + caps.isdbtCaps(isdbtCaps); + mFrontendCaps[5] = caps; + + caps = FrontendInfo::FrontendCapabilities(); + caps.analogCaps(FrontendAnalogCapabilities()); + mFrontendCaps[6] = caps; + + caps = FrontendInfo::FrontendCapabilities(); + caps.atscCaps(FrontendAtscCapabilities()); + mFrontendCaps[7] = caps; + + mLnbs.resize(2); + mLnbs[0] = new Lnb(0); + mLnbs[1] = new Lnb(1); +} + +Tuner::~Tuner() {} + +Return Tuner::getFrontendIds(getFrontendIds_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + vector frontendIds; + frontendIds.resize(mFrontendSize); + for (int i = 0; i < mFrontendSize; i++) { + frontendIds[i] = mFrontends[i]->getFrontendId(); + } + + _hidl_cb(Result::SUCCESS, frontendIds); + return Void(); +} + +Return Tuner::openFrontendById(uint32_t frontendId, openFrontendById_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + if (frontendId >= mFrontendSize || frontendId < 0) { + ALOGW("[ WARN ] Frontend with id %d isn't available", frontendId); + _hidl_cb(Result::UNAVAILABLE, nullptr); + return Void(); + } + + _hidl_cb(Result::SUCCESS, mFrontends[frontendId]); + return Void(); +} + +Return Tuner::openDemux(openDemux_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + uint32_t demuxId = mLastUsedId + 1; + mLastUsedId += 1; + sp demux = new Demux(demuxId, this); + mDemuxes[demuxId] = demux; + + _hidl_cb(Result::SUCCESS, demuxId, demux); + return Void(); +} + +Return Tuner::getDemuxCaps(getDemuxCaps_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + DemuxCapabilities caps; + + // IP filter can be an MMTP filter's data source. + caps.linkCaps = {0x00, 0x00, 0x02, 0x00, 0x00}; + _hidl_cb(Result::SUCCESS, caps); + return Void(); +} + +Return Tuner::openDescrambler(openDescrambler_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + sp descrambler = new Descrambler(); + + _hidl_cb(Result::SUCCESS, descrambler); + return Void(); +} + +Return Tuner::getFrontendInfo(FrontendId frontendId, getFrontendInfo_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + FrontendInfo info; + if (frontendId >= mFrontendSize) { + _hidl_cb(Result::INVALID_ARGUMENT, info); + return Void(); + } + + vector statusCaps = { + FrontendStatusType::DEMOD_LOCK, + FrontendStatusType::SNR, + FrontendStatusType::FEC, + FrontendStatusType::MODULATION, + FrontendStatusType::PLP_ID, + FrontendStatusType::LAYER_ERROR, + FrontendStatusType::ATSC3_PLP_INFO, + }; + // assign randomly selected values for testing. + info = { + .type = mFrontends[frontendId]->getFrontendType(), + .minFrequency = 139, + .maxFrequency = 1139, + .minSymbolRate = 45, + .maxSymbolRate = 1145, + .acquireRange = 30, + .exclusiveGroupId = 57, + .statusCaps = statusCaps, + .frontendCaps = mFrontendCaps[frontendId], + }; + + _hidl_cb(Result::SUCCESS, info); + return Void(); +} + +Return Tuner::getLnbIds(getLnbIds_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + vector lnbIds; + lnbIds.resize(mLnbs.size()); + for (int i = 0; i < lnbIds.size(); i++) { + lnbIds[i] = mLnbs[i]->getId(); + } + + _hidl_cb(Result::SUCCESS, lnbIds); + return Void(); +} + +Return Tuner::openLnbById(V1_0::LnbId lnbId, openLnbById_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + if (lnbId >= mLnbs.size()) { + _hidl_cb(Result::INVALID_ARGUMENT, nullptr); + return Void(); + } + + _hidl_cb(Result::SUCCESS, mLnbs[lnbId]); + return Void(); +} + +sp Tuner::getFrontendById(uint32_t frontendId) { + ALOGV("%s", __FUNCTION__); + + return mFrontends[frontendId]; +} + +Return Tuner::openLnbByName(const hidl_string& /*lnbName*/, openLnbByName_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + sp lnb = new Lnb(); + + _hidl_cb(Result::SUCCESS, 1234, lnb); + return Void(); +} + +void Tuner::setFrontendAsDemuxSource(uint32_t frontendId, uint32_t demuxId) { + mFrontendToDemux[frontendId] = demuxId; + if (mFrontends[frontendId] != nullptr && mFrontends[frontendId]->isLocked()) { + mDemuxes[demuxId]->startFrontendInputLoop(); + } +} + +void Tuner::frontendStopTune(uint32_t frontendId) { + map::iterator it = mFrontendToDemux.find(frontendId); + uint32_t demuxId; + if (it != mFrontendToDemux.end()) { + demuxId = it->second; + mDemuxes[demuxId]->stopFrontendInput(); + } +} + +void Tuner::frontendStartTune(uint32_t frontendId) { + map::iterator it = mFrontendToDemux.find(frontendId); + uint32_t demuxId; + if (it != mFrontendToDemux.end()) { + demuxId = it->second; + mDemuxes[demuxId]->startFrontendInputLoop(); + } +} + +} // namespace implementation +} // namespace V1_0 +} // namespace tuner +} // namespace tv +} // namespace hardware +} // namespace android diff --git a/tv/tuner/1.1/default/Tuner.h b/tv/tuner/1.1/default/Tuner.h new file mode 100644 index 0000000000..9463278a1e --- /dev/null +++ b/tv/tuner/1.1/default/Tuner.h @@ -0,0 +1,94 @@ +/* + * Copyright 2020 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_1_TUNER_H_ +#define ANDROID_HARDWARE_TV_TUNER_V1_1_TUNER_H_ + +#include +#include +#include "Demux.h" +#include "Frontend.h" +#include "Lnb.h" + +using namespace std; + +namespace android { +namespace hardware { +namespace tv { +namespace tuner { +namespace V1_0 { +namespace implementation { + +using ::android::hardware::tv::tuner::V1_1::ITuner; + +class Frontend; +class Demux; +class Lnb; + +class Tuner : public ITuner { + public: + Tuner(); + + virtual Return getFrontendIds(getFrontendIds_cb _hidl_cb) override; + + virtual Return openFrontendById(uint32_t frontendId, + openFrontendById_cb _hidl_cb) override; + + virtual Return openDemux(openDemux_cb _hidl_cb) override; + + virtual Return getDemuxCaps(getDemuxCaps_cb _hidl_cb) override; + + virtual Return openDescrambler(openDescrambler_cb _hidl_cb) override; + + virtual Return getFrontendInfo(uint32_t frontendId, getFrontendInfo_cb _hidl_cb) override; + + virtual Return getLnbIds(getLnbIds_cb _hidl_cb) override; + + virtual Return openLnbById(uint32_t lnbId, openLnbById_cb _hidl_cb) override; + + virtual Return openLnbByName(const hidl_string& lnbName, + openLnbByName_cb _hidl_cb) override; + + sp getFrontendById(uint32_t frontendId); + + void setFrontendAsDemuxSource(uint32_t frontendId, uint32_t demuxId); + + void frontendStartTune(uint32_t frontendId); + void frontendStopTune(uint32_t frontendId); + + private: + virtual ~Tuner(); + // Static mFrontends array to maintain local frontends information + vector> mFrontends; + vector mFrontendCaps; + std::map mFrontendToDemux; + std::map> mDemuxes; + // To maintain how many Frontends we have + int mFrontendSize; + // The last used demux id. Initial value is -1. + // First used id will be 0. + uint32_t mLastUsedId = -1; + vector> mLnbs; +}; + +} // namespace implementation +} // namespace V1_0 +} // namespace tuner +} // namespace tv +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_TV_TUNER_V1_1_TUNER_H_ diff --git a/tv/tuner/1.1/default/android.hardware.tv.tuner@1.1-service-lazy.rc b/tv/tuner/1.1/default/android.hardware.tv.tuner@1.1-service-lazy.rc new file mode 100644 index 0000000000..9e228f7d58 --- /dev/null +++ b/tv/tuner/1.1/default/android.hardware.tv.tuner@1.1-service-lazy.rc @@ -0,0 +1,9 @@ +service vendor.tuner-hal-1-1 /vendor/bin/hw/android.hardware.tv.tuner@1.1-service-lazy + interface android.hardware.tv.tuner@1.1::ITuner default + oneshot + disabled + class hal + user media + group mediadrm drmrpc + ioprio rt 4 + writepid /dev/cpuset/foreground/tasks \ No newline at end of file diff --git a/tv/tuner/1.1/default/android.hardware.tv.tuner@1.1-service-lazy.xml b/tv/tuner/1.1/default/android.hardware.tv.tuner@1.1-service-lazy.xml new file mode 100644 index 0000000000..86b044525f --- /dev/null +++ b/tv/tuner/1.1/default/android.hardware.tv.tuner@1.1-service-lazy.xml @@ -0,0 +1,11 @@ + + + android.hardware.tv.tuner + hwbinder + 1.1 + + ITuner + default + + + \ No newline at end of file diff --git a/tv/tuner/1.1/default/android.hardware.tv.tuner@1.1-service.rc b/tv/tuner/1.1/default/android.hardware.tv.tuner@1.1-service.rc new file mode 100644 index 0000000000..3718a9388f --- /dev/null +++ b/tv/tuner/1.1/default/android.hardware.tv.tuner@1.1-service.rc @@ -0,0 +1,6 @@ +service vendor.tuner-hal-1-1 /vendor/bin/hw/android.hardware.tv.tuner@1.1-service + class hal + user media + group mediadrm drmrpc + ioprio rt 4 + writepid /dev/cpuset/foreground/tasks \ No newline at end of file diff --git a/tv/tuner/1.1/default/android.hardware.tv.tuner@1.1-service.xml b/tv/tuner/1.1/default/android.hardware.tv.tuner@1.1-service.xml new file mode 100644 index 0000000000..86b044525f --- /dev/null +++ b/tv/tuner/1.1/default/android.hardware.tv.tuner@1.1-service.xml @@ -0,0 +1,11 @@ + + + android.hardware.tv.tuner + hwbinder + 1.1 + + ITuner + default + + + \ No newline at end of file diff --git a/tv/tuner/1.1/default/service.cpp b/tv/tuner/1.1/default/service.cpp new file mode 100644 index 0000000000..232030850d --- /dev/null +++ b/tv/tuner/1.1/default/service.cpp @@ -0,0 +1,57 @@ +/* + * Copyright 2020 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_NDEBUG 0 +#ifdef LAZY_SERVICE +#define LOG_TAG "android.hardware.tv.tuner@1.1-service-lazy" +#else +#define LOG_TAG "android.hardware.tv.tuner@1.1-service" +#endif + +#include +#include + +#include "Tuner.h" + +using android::hardware::configureRpcThreadpool; +using android::hardware::joinRpcThreadpool; +using android::hardware::LazyServiceRegistrar; +using android::hardware::tv::tuner::V1_0::implementation::Tuner; +using android::hardware::tv::tuner::V1_1::ITuner; + +#ifdef LAZY_SERVICE +const bool kLazyService = true; +#else +const bool kLazyService = false; +#endif + +int main() { + configureRpcThreadpool(8, true /* callerWillJoin */); + + // Setup hwbinder service + android::sp service = new Tuner(); + android::status_t status; + if (kLazyService) { + auto serviceRegistrar = LazyServiceRegistrar::getInstance(); + status = serviceRegistrar.registerService(service); + } else { + status = service->registerAsService(); + } + LOG_ALWAYS_FATAL_IF(status != android::OK, "Error while registering tuner service: %d", status); + + joinRpcThreadpool(); + return 0; +} From 45cc57ac67adc06d86ac6ae1b2c56a6ff27de007 Mon Sep 17 00:00:00 2001 From: Amy Zhang Date: Thu, 9 Jul 2020 22:56:25 -0700 Subject: [PATCH 3/3] Add VTS of Tuner HAL 1.1 For the APIs that have both 1.0 and 1.1 versions, the VTS 1.1 only tests the 1.1 version of them. For example for getId in IFilter 1.0 and getId_64bit in IFilter 1.1, the 1.1 VTS only tests the getId_64bit. Test: atest VtsHalTvTunerV1_1TargetTest Bug: b/159058358 Change-Id: Ibef0ef40c3d1fbb9ff034bfcabeb44663a444d23 --- tv/tuner/1.1/TEST_MAPPING | 7 + tv/tuner/1.1/vts/OWNERS | 4 + tv/tuner/1.1/vts/functional/Android.bp | 48 +++++ tv/tuner/1.1/vts/functional/DemuxTests.cpp | 41 ++++ tv/tuner/1.1/vts/functional/DemuxTests.h | 56 ++++++ tv/tuner/1.1/vts/functional/FilterTests.cpp | 111 +++++++++++ tv/tuner/1.1/vts/functional/FilterTests.h | 168 +++++++++++++++++ tv/tuner/1.1/vts/functional/FrontendTests.cpp | 83 ++++++++ tv/tuner/1.1/vts/functional/FrontendTests.h | 96 ++++++++++ .../VtsHalTvTunerV1_1TargetTest.cpp | 58 ++++++ .../functional/VtsHalTvTunerV1_1TargetTest.h | 52 +++++ .../VtsHalTvTunerV1_1TestConfigurations.h | 178 ++++++++++++++++++ 12 files changed, 902 insertions(+) create mode 100644 tv/tuner/1.1/TEST_MAPPING create mode 100644 tv/tuner/1.1/vts/OWNERS create mode 100644 tv/tuner/1.1/vts/functional/Android.bp create mode 100644 tv/tuner/1.1/vts/functional/DemuxTests.cpp create mode 100644 tv/tuner/1.1/vts/functional/DemuxTests.h create mode 100644 tv/tuner/1.1/vts/functional/FilterTests.cpp create mode 100644 tv/tuner/1.1/vts/functional/FilterTests.h create mode 100644 tv/tuner/1.1/vts/functional/FrontendTests.cpp create mode 100644 tv/tuner/1.1/vts/functional/FrontendTests.h create mode 100644 tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp create mode 100644 tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h create mode 100644 tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h diff --git a/tv/tuner/1.1/TEST_MAPPING b/tv/tuner/1.1/TEST_MAPPING new file mode 100644 index 0000000000..7c91b8fe6c --- /dev/null +++ b/tv/tuner/1.1/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "VtsHalTvTunerV1_1TargetTest" + } + ] +} \ No newline at end of file diff --git a/tv/tuner/1.1/vts/OWNERS b/tv/tuner/1.1/vts/OWNERS new file mode 100644 index 0000000000..1b3d095f9c --- /dev/null +++ b/tv/tuner/1.1/vts/OWNERS @@ -0,0 +1,4 @@ +nchalko@google.com +amyjojo@google.com +shubang@google.com +quxiangfang@google.com diff --git a/tv/tuner/1.1/vts/functional/Android.bp b/tv/tuner/1.1/vts/functional/Android.bp new file mode 100644 index 0000000000..7ab0f87119 --- /dev/null +++ b/tv/tuner/1.1/vts/functional/Android.bp @@ -0,0 +1,48 @@ +// +// Copyright 2020 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. +// + +cc_test { + name: "VtsHalTvTunerV1_1TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: [ + "VtsHalTvTunerV1_1TargetTest.cpp", + "FrontendTests.cpp", + "DemuxTests.cpp", + "FilterTests.cpp", + ], + static_libs: [ + "android.hardware.cas@1.0", + "android.hardware.cas@1.1", + "android.hardware.cas@1.2", + "android.hardware.tv.tuner@1.0", + "android.hardware.tv.tuner@1.1", + "android.hidl.allocator@1.0", + "android.hidl.memory@1.0", + "libhidlallocatorutils", + "libhidlmemory", + "libcutils", + "libfmq", + ], + shared_libs: [ + "libbinder", + ], + test_suites: [ + "general-tests", + "vts", + ], + + require_root: true, +} diff --git a/tv/tuner/1.1/vts/functional/DemuxTests.cpp b/tv/tuner/1.1/vts/functional/DemuxTests.cpp new file mode 100644 index 0000000000..b1d8a0a0b2 --- /dev/null +++ b/tv/tuner/1.1/vts/functional/DemuxTests.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2020 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. + */ + +#include "DemuxTests.h" + +AssertionResult DemuxTests::openDemux(sp& demux, uint32_t& demuxId) { + Result status; + mService->openDemux([&](Result result, uint32_t id, const sp& demuxSp) { + mDemux = demuxSp; + demux = demuxSp; + demuxId = id; + status = result; + }); + return AssertionResult(status == Result::SUCCESS); +} + +AssertionResult DemuxTests::setDemuxFrontendDataSource(uint32_t frontendId) { + EXPECT_TRUE(mDemux) << "Test with openDemux first."; + auto status = mDemux->setFrontendDataSource(frontendId); + return AssertionResult(status.isOk()); +} + +AssertionResult DemuxTests::closeDemux() { + EXPECT_TRUE(mDemux) << "Test with openDemux first."; + auto status = mDemux->close(); + mDemux = nullptr; + return AssertionResult(status.isOk()); +} \ No newline at end of file diff --git a/tv/tuner/1.1/vts/functional/DemuxTests.h b/tv/tuner/1.1/vts/functional/DemuxTests.h new file mode 100644 index 0000000000..c28d6ca524 --- /dev/null +++ b/tv/tuner/1.1/vts/functional/DemuxTests.h @@ -0,0 +1,56 @@ +/* + * Copyright 2020 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using android::sp; +using android::hardware::Return; +using android::hardware::Void; +using android::hardware::tv::tuner::V1_0::IDemux; +using android::hardware::tv::tuner::V1_0::IFilter; +using android::hardware::tv::tuner::V1_0::Result; +using android::hardware::tv::tuner::V1_1::ITuner; + +using ::testing::AssertionResult; + +class DemuxTests { + public: + void setService(sp tuner) { mService = tuner; } + + AssertionResult openDemux(sp& demux, uint32_t& demuxId); + AssertionResult setDemuxFrontendDataSource(uint32_t frontendId); + AssertionResult closeDemux(); + + protected: + static AssertionResult failure() { return ::testing::AssertionFailure(); } + + static AssertionResult success() { return ::testing::AssertionSuccess(); } + + sp mService; + sp mDemux; +}; diff --git a/tv/tuner/1.1/vts/functional/FilterTests.cpp b/tv/tuner/1.1/vts/functional/FilterTests.cpp new file mode 100644 index 0000000000..fb0c1a5cc8 --- /dev/null +++ b/tv/tuner/1.1/vts/functional/FilterTests.cpp @@ -0,0 +1,111 @@ +/* + * Copyright 2020 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. + */ + +#include "FilterTests.h" + +AssertionResult FilterTests::openFilterInDemux(DemuxFilterType type, uint32_t bufferSize) { + Result status; + EXPECT_TRUE(mDemux) << "Test with openDemux first."; + + // Create demux callback + mFilterCallback = new FilterCallback(); + + // Add filter to the local demux + mDemux->openFilter(type, bufferSize, mFilterCallback, + [&](Result result, const sp& filter) { + mFilter = filter; + status = result; + }); + + return AssertionResult(status == Result::SUCCESS); +} + +AssertionResult FilterTests::getNewlyOpenedFilterId_64bit(uint64_t& filterId) { + Result status; + EXPECT_TRUE(mDemux) << "Test with openDemux first."; + EXPECT_TRUE(mFilter) << "Test with openFilterInDemux first."; + EXPECT_TRUE(mFilterCallback) << "Test with openFilterInDemux first."; + + sp filter_v1_1 = + android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilter); + if (filter_v1_1 != NULL) { + filter_v1_1->getId64Bit([&](Result result, uint64_t filterId) { + mFilterId = filterId; + status = result; + }); + } else { + ALOGW("[vts] Can't cast IFilter into v1_1."); + return failure(); + } + + if (status == Result::SUCCESS) { + mUsedFilterIds.insert(mUsedFilterIds.end(), mFilterId); + mFilters[mFilterId] = mFilter; + mFilterCallbacks[mFilterId] = mFilterCallback; + filterId = mFilterId; + } + + return AssertionResult(status == Result::SUCCESS); +} + +AssertionResult FilterTests::configFilter(DemuxFilterSettings setting, uint64_t filterId) { + Result status; + EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first."; + status = mFilters[filterId]->configure(setting); + + return AssertionResult(status == Result::SUCCESS); +} + +AssertionResult FilterTests::getFilterMQDescriptor(uint64_t filterId) { + Result status; + EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first."; + EXPECT_TRUE(mFilterCallbacks[filterId]) << "Test with getNewlyOpenedFilterId first."; + + mFilter->getQueueDesc([&](Result result, const MQDesc& filterMQDesc) { + mFilterMQDescriptor = filterMQDesc; + status = result; + }); + + return AssertionResult(status == Result::SUCCESS); +} + +AssertionResult FilterTests::startFilter(uint64_t filterId) { + EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first."; + Result status = mFilters[filterId]->start(); + return AssertionResult(status == Result::SUCCESS); +} + +AssertionResult FilterTests::stopFilter(uint64_t filterId) { + EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first."; + Result status = mFilters[filterId]->stop(); + return AssertionResult(status == Result::SUCCESS); +} + +AssertionResult FilterTests::closeFilter(uint64_t filterId) { + EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first."; + Result status = mFilters[filterId]->close(); + if (status == Result::SUCCESS) { + for (int i = 0; i < mUsedFilterIds.size(); i++) { + if (mUsedFilterIds[i] == filterId) { + mUsedFilterIds.erase(mUsedFilterIds.begin() + i); + break; + } + } + mFilterCallbacks.erase(filterId); + mFilters.erase(filterId); + } + return AssertionResult(status == Result::SUCCESS); +} diff --git a/tv/tuner/1.1/vts/functional/FilterTests.h b/tv/tuner/1.1/vts/functional/FilterTests.h new file mode 100644 index 0000000000..b1b6e13759 --- /dev/null +++ b/tv/tuner/1.1/vts/functional/FilterTests.h @@ -0,0 +1,168 @@ +/* + * Copyright 2020 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using android::Condition; +using android::Mutex; +using android::sp; +using android::hardware::EventFlag; +using android::hardware::hidl_handle; +using android::hardware::hidl_string; +using android::hardware::hidl_vec; +using android::hardware::kSynchronizedReadWrite; +using android::hardware::MessageQueue; +using android::hardware::MQDescriptorSync; +using android::hardware::Return; +using android::hardware::Void; +using android::hardware::tv::tuner::V1_0::DemuxFilterEvent; +using android::hardware::tv::tuner::V1_0::DemuxFilterMainType; +using android::hardware::tv::tuner::V1_0::DemuxFilterSettings; +using android::hardware::tv::tuner::V1_0::DemuxFilterStatus; +using android::hardware::tv::tuner::V1_0::DemuxFilterType; +using android::hardware::tv::tuner::V1_0::DemuxTsFilterType; +using android::hardware::tv::tuner::V1_0::IDemux; +using android::hardware::tv::tuner::V1_0::IFilter; +using android::hardware::tv::tuner::V1_0::IFilterCallback; +using android::hardware::tv::tuner::V1_0::Result; +using android::hardware::tv::tuner::V1_1::ITuner; + +using ::testing::AssertionResult; + +using namespace std; + +enum FilterEventType : uint8_t { + UNDEFINED, + SECTION, + MEDIA, + PES, + RECORD, + MMTPRECORD, + DOWNLOAD, + TEMI, +}; + +using FilterMQ = MessageQueue; +using MQDesc = MQDescriptorSync; + +#define WAIT_TIMEOUT 3000000000 + +class FilterCallback : public IFilterCallback { + public: + virtual Return onFilterEvent(const DemuxFilterEvent& /*filterEvent*/) override { + return Void(); + } + + virtual Return onFilterStatus(const DemuxFilterStatus /*status*/) override { + return Void(); + } +}; + +class FilterTests { + public: + void setService(sp tuner) { mService = tuner; } + void setDemux(sp demux) { mDemux = demux; } + sp getFilterById(uint64_t filterId) { return mFilters[filterId]; } + + std::map> getFilterCallbacks() { return mFilterCallbacks; } + + AssertionResult openFilterInDemux(DemuxFilterType type, uint32_t bufferSize); + AssertionResult getNewlyOpenedFilterId_64bit(uint64_t& filterId); + AssertionResult configFilter(DemuxFilterSettings setting, uint64_t filterId); + AssertionResult getFilterMQDescriptor(uint64_t filterId); + AssertionResult startFilter(uint64_t filterId); + AssertionResult stopFilter(uint64_t filterId); + AssertionResult closeFilter(uint64_t filterId); + + FilterEventType getFilterEventType(DemuxFilterType type) { + FilterEventType eventType = FilterEventType::UNDEFINED; + switch (type.mainType) { + case DemuxFilterMainType::TS: + switch (type.subType.tsFilterType()) { + case DemuxTsFilterType::UNDEFINED: + break; + case DemuxTsFilterType::SECTION: + eventType = FilterEventType::SECTION; + break; + case DemuxTsFilterType::PES: + eventType = FilterEventType::PES; + break; + case DemuxTsFilterType::TS: + break; + case DemuxTsFilterType::AUDIO: + case DemuxTsFilterType::VIDEO: + eventType = FilterEventType::MEDIA; + break; + case DemuxTsFilterType::PCR: + break; + case DemuxTsFilterType::RECORD: + eventType = FilterEventType::RECORD; + break; + case DemuxTsFilterType::TEMI: + eventType = FilterEventType::TEMI; + 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 eventType; + } + + protected: + static AssertionResult failure() { return ::testing::AssertionFailure(); } + + static AssertionResult success() { return ::testing::AssertionSuccess(); } + + sp mService; + sp mFilter; + sp mDemux; + std::map> mFilters; + std::map> mFilterCallbacks; + + sp mFilterCallback; + MQDesc mFilterMQDescriptor; + vector mUsedFilterIds; + + uint64_t mFilterId = -1; +}; diff --git a/tv/tuner/1.1/vts/functional/FrontendTests.cpp b/tv/tuner/1.1/vts/functional/FrontendTests.cpp new file mode 100644 index 0000000000..8c359c51a6 --- /dev/null +++ b/tv/tuner/1.1/vts/functional/FrontendTests.cpp @@ -0,0 +1,83 @@ +/* + * Copyright 2020 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. + */ + +#include "FrontendTests.h" + +Return FrontendCallback::onEvent(FrontendEventType /*frontendEventType*/) { + return Void(); +} + +Return FrontendCallback::onScanMessage(FrontendScanMessageType /*type*/, + const FrontendScanMessage& /*message*/) { + return Void(); +} + +AssertionResult FrontendTests::getFrontendIds() { + Result status; + mService->getFrontendIds([&](Result result, const hidl_vec& frontendIds) { + status = result; + mFeIds = frontendIds; + }); + return AssertionResult(status == Result::SUCCESS); +} + +AssertionResult FrontendTests::getFrontendInfo(uint32_t frontendId) { + Result status; + mService->getFrontendInfo(frontendId, [&](Result result, const FrontendInfo& frontendInfo) { + mFrontendInfo = frontendInfo; + status = result; + }); + return AssertionResult(status == Result::SUCCESS); +} + +AssertionResult FrontendTests::openFrontendById(uint32_t frontendId) { + Result status; + mService->openFrontendById(frontendId, [&](Result result, const sp& frontend) { + mFrontend = frontend; + status = result; + }); + return AssertionResult(status == Result::SUCCESS); +} + +AssertionResult FrontendTests::setFrontendCallback() { + EXPECT_TRUE(mFrontend) << "Test with openFrontendById first."; + mFrontendCallback = new FrontendCallback(); + auto callbackStatus = mFrontend->setCallback(mFrontendCallback); + return AssertionResult(callbackStatus.isOk()); +} + +AssertionResult FrontendTests::closeFrontend() { + EXPECT_TRUE(mFrontend) << "Test with openFrontendById first."; + Result status; + status = mFrontend->close(); + mFrontend = nullptr; + mFrontendCallback = nullptr; + return AssertionResult(status == Result::SUCCESS); +} + +void FrontendTests::getFrontendIdByType(FrontendType feType, uint32_t& feId) { + ASSERT_TRUE(getFrontendIds()); + ASSERT_TRUE(mFeIds.size() > 0); + for (size_t i = 0; i < mFeIds.size(); i++) { + ASSERT_TRUE(getFrontendInfo(mFeIds[i])); + if (mFrontendInfo.type != feType) { + continue; + } + feId = mFeIds[i]; + return; + } + feId = INVALID_ID; +} \ No newline at end of file diff --git a/tv/tuner/1.1/vts/functional/FrontendTests.h b/tv/tuner/1.1/vts/functional/FrontendTests.h new file mode 100644 index 0000000000..e68758919d --- /dev/null +++ b/tv/tuner/1.1/vts/functional/FrontendTests.h @@ -0,0 +1,96 @@ +/* + * Copyright 2020 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "VtsHalTvTunerV1_1TestConfigurations.h" + +#define WAIT_TIMEOUT 3000000000 +#define INVALID_ID -1 + +using android::Condition; +using android::IMemory; +using android::IMemoryHeap; +using android::MemoryDealer; +using android::Mutex; +using android::sp; +using android::hardware::fromHeap; +using android::hardware::hidl_vec; +using android::hardware::Return; +using android::hardware::Void; +using android::hardware::tv::tuner::V1_0::FrontendEventType; +using android::hardware::tv::tuner::V1_0::FrontendId; +using android::hardware::tv::tuner::V1_0::FrontendInfo; +using android::hardware::tv::tuner::V1_0::FrontendScanMessage; +using android::hardware::tv::tuner::V1_0::FrontendScanMessageType; +using android::hardware::tv::tuner::V1_0::IFrontend; +using android::hardware::tv::tuner::V1_0::IFrontendCallback; +using android::hardware::tv::tuner::V1_0::Result; +using android::hardware::tv::tuner::V1_1::ITuner; + +using ::testing::AssertionResult; + +using namespace std; + +#define INVALID_ID -1 + +class FrontendCallback : public IFrontendCallback { + public: + virtual Return onEvent(FrontendEventType frontendEventType) override; + virtual Return onScanMessage(FrontendScanMessageType type, + const FrontendScanMessage& message) override; +}; + +class FrontendTests { + public: + sp mService; + + void setService(sp tuner) { mService = tuner; } + + AssertionResult getFrontendIds(); + AssertionResult getFrontendInfo(uint32_t frontendId); + AssertionResult openFrontendById(uint32_t frontendId); + AssertionResult setFrontendCallback(); + AssertionResult closeFrontend(); + + void getFrontendIdByType(FrontendType feType, uint32_t& feId); + + protected: + static AssertionResult failure() { return ::testing::AssertionFailure(); } + static AssertionResult success() { return ::testing::AssertionSuccess(); } + + sp mFrontend; + FrontendInfo mFrontendInfo; + sp mFrontendCallback; + hidl_vec mFeIds; + + bool mIsSoftwareFe = false; +}; \ No newline at end of file diff --git a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp new file mode 100644 index 0000000000..7c113d4421 --- /dev/null +++ b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp @@ -0,0 +1,58 @@ +/* + * Copyright 2020 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. + */ + +#include "VtsHalTvTunerV1_1TargetTest.h" + +namespace { + +void TunerFilterHidlTest::configSingleFilterInDemuxTest(FilterConfig filterConf, + FrontendConfig frontendConf) { + uint32_t feId; + uint32_t demuxId; + sp demux; + uint64_t filterId; + + mFrontendTests.getFrontendIdByType(frontendConf.type, feId); + ASSERT_TRUE(feId != INVALID_ID); + ASSERT_TRUE(mFrontendTests.openFrontendById(feId)); + ASSERT_TRUE(mFrontendTests.setFrontendCallback()); + ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId)); + ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId)); + mFilterTests.setDemux(demux); + ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize)); + ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId)); + ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId)); + ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId)); + ASSERT_TRUE(mFilterTests.startFilter(filterId)); + ASSERT_TRUE(mFilterTests.stopFilter(filterId)); + ASSERT_TRUE(mFilterTests.closeFilter(filterId)); + ASSERT_TRUE(mDemuxTests.closeDemux()); + ASSERT_TRUE(mFrontendTests.closeFrontend()); +} + +TEST_P(TunerFilterHidlTest, StartFilterInDemux) { + description("Open and start a filter in Demux."); + // TODO use parameterized tests + configSingleFilterInDemuxTest(filterArray[TS_VIDEO0], frontendArray[DVBT]); +} + +INSTANTIATE_TEST_SUITE_P( + PerInstance, TunerFilterHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)), + android::hardware::PrintInstanceNameToString); + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerFilterHidlTest); +} // namespace diff --git a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h new file mode 100644 index 0000000000..04536343fa --- /dev/null +++ b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h @@ -0,0 +1,52 @@ +/* + * Copyright 2020 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. + */ + +#include "DemuxTests.h" +#include "FilterTests.h" +#include "FrontendTests.h" + +namespace { + +void initConfiguration() { + initFrontendConfig(); + initFilterConfig(); +} + +class TunerFilterHidlTest : public testing::TestWithParam { + public: + virtual void SetUp() override { + mService = ITuner::getService(GetParam()); + ASSERT_NE(mService, nullptr); + initConfiguration(); + + mFrontendTests.setService(mService); + mDemuxTests.setService(mService); + mFilterTests.setService(mService); + } + + protected: + static void description(const std::string& description) { + RecordProperty("description", description); + } + + void configSingleFilterInDemuxTest(FilterConfig filterConf, FrontendConfig frontendConf); + + sp mService; + FrontendTests mFrontendTests; + DemuxTests mDemuxTests; + FilterTests mFilterTests; +}; +} // namespace \ No newline at end of file diff --git a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h new file mode 100644 index 0000000000..23c9353d03 --- /dev/null +++ b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h @@ -0,0 +1,178 @@ +/* + * Copyright 2020 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. + */ + +#include +#include +#include +#include +#include +#include + +using android::hardware::tv::tuner::V1_0::DemuxAlpFilterType; +using android::hardware::tv::tuner::V1_0::DemuxFilterEvent; +using android::hardware::tv::tuner::V1_0::DemuxFilterMainType; +using android::hardware::tv::tuner::V1_0::DemuxFilterSettings; +using android::hardware::tv::tuner::V1_0::DemuxFilterType; +using android::hardware::tv::tuner::V1_0::DemuxIpFilterType; +using android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType; +using android::hardware::tv::tuner::V1_0::DemuxRecordScIndexType; +using android::hardware::tv::tuner::V1_0::DemuxTsFilterType; +using android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth; +using android::hardware::tv::tuner::V1_0::FrontendDvbtCoderate; +using android::hardware::tv::tuner::V1_0::FrontendDvbtConstellation; +using android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval; +using android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy; +using android::hardware::tv::tuner::V1_0::FrontendDvbtSettings; +using android::hardware::tv::tuner::V1_0::FrontendDvbtStandard; +using android::hardware::tv::tuner::V1_0::FrontendDvbtTransmissionMode; +using android::hardware::tv::tuner::V1_0::FrontendSettings; +using android::hardware::tv::tuner::V1_0::FrontendStatus; +using android::hardware::tv::tuner::V1_0::FrontendStatusType; +using android::hardware::tv::tuner::V1_0::FrontendType; + +using namespace std; + +const uint32_t FMQ_SIZE_1M = 0x100000; +const uint32_t FMQ_SIZE_4M = 0x400000; +const uint32_t FMQ_SIZE_16M = 0x1000000; + +typedef enum { + TS_VIDEO0, + TS_VIDEO1, + TS_AUDIO0, + TS_AUDIO1, + TS_PES0, + TS_PCR0, + TS_SECTION0, + TS_TS0, + TS_RECORD0, + FILTER_MAX, +} Filter; + +typedef enum { + DVBT, + DVBS, + FRONTEND_MAX, +} Frontend; + +struct FilterConfig { + uint32_t bufferSize; + DemuxFilterType type; + DemuxFilterSettings settings; + + bool operator<(const FilterConfig& /*c*/) const { return false; } +}; + +struct FrontendConfig { + bool isSoftwareFe; + FrontendType type; + FrontendSettings settings; + vector tuneStatusTypes; + vector expectTuneStatuses; +}; + +static FrontendConfig frontendArray[FILTER_MAX]; +static FilterConfig filterArray[FILTER_MAX]; + +/** Configuration array for the frontend tune test */ +inline void initFrontendConfig() { + FrontendDvbtSettings dvbtSettings{ + .frequency = 578000, + .transmissionMode = FrontendDvbtTransmissionMode::AUTO, + .bandwidth = FrontendDvbtBandwidth::BANDWIDTH_8MHZ, + .constellation = FrontendDvbtConstellation::AUTO, + .hierarchy = FrontendDvbtHierarchy::AUTO, + .hpCoderate = FrontendDvbtCoderate::AUTO, + .lpCoderate = FrontendDvbtCoderate::AUTO, + .guardInterval = FrontendDvbtGuardInterval::AUTO, + .isHighPriority = true, + .standard = FrontendDvbtStandard::T, + }; + frontendArray[DVBT].type = FrontendType::DVBT, frontendArray[DVBT].settings.dvbt(dvbtSettings); + vector types; + types.push_back(FrontendStatusType::DEMOD_LOCK); + FrontendStatus status; + status.isDemodLocked(true); + vector statuses; + statuses.push_back(status); + frontendArray[DVBT].tuneStatusTypes = types; + frontendArray[DVBT].expectTuneStatuses = statuses; + frontendArray[DVBT].isSoftwareFe = true; + frontendArray[DVBS].type = FrontendType::DVBS; + frontendArray[DVBS].isSoftwareFe = true; +}; + +/** Configuration array for the filter test */ +inline void initFilterConfig() { + // TS VIDEO filter setting for default implementation testing + filterArray[TS_VIDEO0].type.mainType = DemuxFilterMainType::TS; + filterArray[TS_VIDEO0].type.subType.tsFilterType(DemuxTsFilterType::VIDEO); + filterArray[TS_VIDEO0].bufferSize = FMQ_SIZE_16M; + filterArray[TS_VIDEO0].settings.ts().tpid = 256; + filterArray[TS_VIDEO0].settings.ts().filterSettings.av({.isPassthrough = false}); + filterArray[TS_VIDEO1].type.mainType = DemuxFilterMainType::TS; + filterArray[TS_VIDEO1].type.subType.tsFilterType(DemuxTsFilterType::VIDEO); + filterArray[TS_VIDEO1].bufferSize = FMQ_SIZE_16M; + filterArray[TS_VIDEO1].settings.ts().tpid = 256; + filterArray[TS_VIDEO1].settings.ts().filterSettings.av({.isPassthrough = false}); + // TS AUDIO filter setting + filterArray[TS_AUDIO0].type.mainType = DemuxFilterMainType::TS; + filterArray[TS_AUDIO0].type.subType.tsFilterType(DemuxTsFilterType::AUDIO); + filterArray[TS_AUDIO0].bufferSize = FMQ_SIZE_16M; + filterArray[TS_AUDIO0].settings.ts().tpid = 256; + filterArray[TS_AUDIO0].settings.ts().filterSettings.av({.isPassthrough = false}); + filterArray[TS_AUDIO1].type.mainType = DemuxFilterMainType::TS; + filterArray[TS_AUDIO1].type.subType.tsFilterType(DemuxTsFilterType::AUDIO); + filterArray[TS_AUDIO1].bufferSize = FMQ_SIZE_16M; + filterArray[TS_AUDIO1].settings.ts().tpid = 257; + filterArray[TS_AUDIO1].settings.ts().filterSettings.av({.isPassthrough = false}); + // TS PES filter setting + filterArray[TS_PES0].type.mainType = DemuxFilterMainType::TS; + filterArray[TS_PES0].type.subType.tsFilterType(DemuxTsFilterType::PES); + filterArray[TS_PES0].bufferSize = FMQ_SIZE_16M; + filterArray[TS_PES0].settings.ts().tpid = 256; + filterArray[TS_PES0].settings.ts().filterSettings.pesData({ + .isRaw = false, + .streamId = 0xbd, + }); + // TS PCR filter setting + filterArray[TS_PCR0].type.mainType = DemuxFilterMainType::TS; + filterArray[TS_PCR0].type.subType.tsFilterType(DemuxTsFilterType::PCR); + filterArray[TS_PCR0].bufferSize = FMQ_SIZE_16M; + filterArray[TS_PCR0].settings.ts().tpid = 256; + filterArray[TS_PCR0].settings.ts().filterSettings.noinit(); + // TS filter setting + filterArray[TS_TS0].type.mainType = DemuxFilterMainType::TS; + filterArray[TS_TS0].type.subType.tsFilterType(DemuxTsFilterType::TS); + filterArray[TS_TS0].bufferSize = FMQ_SIZE_16M; + filterArray[TS_TS0].settings.ts().tpid = 256; + filterArray[TS_TS0].settings.ts().filterSettings.noinit(); + // TS SECTION filter setting + filterArray[TS_SECTION0].type.mainType = DemuxFilterMainType::TS; + filterArray[TS_SECTION0].type.subType.tsFilterType(DemuxTsFilterType::SECTION); + filterArray[TS_SECTION0].bufferSize = FMQ_SIZE_16M; + filterArray[TS_SECTION0].settings.ts().tpid = 256; + filterArray[TS_SECTION0].settings.ts().filterSettings.section({ + .isRaw = false, + }); + // TS RECORD filter setting + filterArray[TS_RECORD0].type.mainType = DemuxFilterMainType::TS; + filterArray[TS_RECORD0].type.subType.tsFilterType(DemuxTsFilterType::RECORD); + filterArray[TS_RECORD0].settings.ts().tpid = 81; + filterArray[TS_RECORD0].settings.ts().filterSettings.record({ + .scIndexType = DemuxRecordScIndexType::NONE, + }); +};