Loading tv/tuner/1.0/vts/functional/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ cc_test { srcs: [ "VtsHalTvTunerV1_0TargetTest.cpp", "FrontendTests.cpp", "DemuxTests.cpp", "FilterTests.cpp", ], static_libs: [ "android.hardware.tv.tuner@1.0", Loading tv/tuner/1.0/vts/functional/DemuxTests.cpp 0 → 100644 +41 −0 Original line number Diff line number Diff line /* * 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<IDemux>& demux, uint32_t& demuxId) { Result status; mService->openDemux([&](Result result, uint32_t id, const sp<IDemux>& 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 tv/tuner/1.0/vts/functional/DemuxTests.h 0 → 100644 +57 −0 Original line number Diff line number Diff line /* * 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 <VtsHalHidlTargetTestBase.h> #include <VtsHalHidlTargetTestEnvBase.h> #include <android-base/logging.h> #include <android/hardware/tv/tuner/1.0/IDemux.h> #include <android/hardware/tv/tuner/1.0/ITuner.h> #include <android/hardware/tv/tuner/1.0/types.h> #include <binder/MemoryDealer.h> #include <gtest/gtest.h> #include <hidl/ServiceManagement.h> #include <hidl/Status.h> #include <hidlmemory/FrameworkUtils.h> #include <utils/Condition.h> #include <utils/Mutex.h> #include <map> 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::ITuner; using android::hardware::tv::tuner::V1_0::Result; using ::testing::AssertionResult; class DemuxTests { public: sp<ITuner> mService; void setService(sp<ITuner> tuner) { mService = tuner; } AssertionResult openDemux(sp<IDemux>& 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<IDemux> mDemux; }; No newline at end of file tv/tuner/1.0/vts/functional/FilterTests.cpp 0 → 100644 +226 −0 Original line number Diff line number Diff line /* * 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" void FilterCallback::startFilterEventThread(DemuxFilterEvent event) { struct FilterThreadArgs* threadArgs = (struct FilterThreadArgs*)malloc(sizeof(struct FilterThreadArgs)); threadArgs->user = this; threadArgs->event = event; pthread_create(&mFilterThread, NULL, __threadLoopFilter, (void*)threadArgs); pthread_setname_np(mFilterThread, "test_playback_input_loop"); } void FilterCallback::testFilterDataOutput() { android::Mutex::Autolock autoLock(mMsgLock); while (mPidFilterOutputCount < 1) { if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) { EXPECT_TRUE(false) << "filter output matching pid does not output within timeout"; return; } } mPidFilterOutputCount = 0; ALOGW("[vts] pass and stop"); } void FilterCallback::updateFilterMQ(MQDesc& filterMQDescriptor) { mFilterMQ = std::make_unique<FilterMQ>(filterMQDescriptor, true /* resetPointers */); EXPECT_TRUE(mFilterMQ); EXPECT_TRUE(EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag) == android::OK); } void FilterCallback::updateGoldenOutputMap(string goldenOutputFile) { mFilterIdToGoldenOutput = goldenOutputFile; } void* FilterCallback::__threadLoopFilter(void* threadArgs) { FilterCallback* const self = static_cast<FilterCallback*>(((struct FilterThreadArgs*)threadArgs)->user); self->filterThreadLoop(((struct FilterThreadArgs*)threadArgs)->event); return 0; } void FilterCallback::filterThreadLoop(DemuxFilterEvent& /* event */) { android::Mutex::Autolock autoLock(mFilterOutputLock); // Read from mFilterMQ[event.filterId] per event and filter type // Assemble to filterOutput[filterId] // check if filterOutput[filterId] matches goldenOutput[filterId] // If match, remove filterId entry from MQ map // end thread } bool FilterCallback::readFilterEventData() { bool result = false; DemuxFilterEvent filterEvent = mFilterEvent; ALOGW("[vts] reading from filter FMQ or buffer %d", mFilterId); // todo separate filter handlers for (int i = 0; i < filterEvent.events.size(); i++) { switch (mFilterEventType) { case FilterEventType::SECTION: mDataLength = filterEvent.events[i].section().dataLength; break; case FilterEventType::PES: mDataLength = filterEvent.events[i].pes().dataLength; break; case FilterEventType::MEDIA: return dumpAvData(filterEvent.events[i].media()); case FilterEventType::RECORD: break; case FilterEventType::MMTPRECORD: break; case FilterEventType::DOWNLOAD: break; default: break; } // EXPECT_TRUE(mDataLength == goldenDataOutputBuffer.size()) << "buffer size does not // match"; mDataOutputBuffer.resize(mDataLength); result = mFilterMQ->read(mDataOutputBuffer.data(), mDataLength); EXPECT_TRUE(result) << "can't read from Filter MQ"; /*for (int i = 0; i < mDataLength; i++) { EXPECT_TRUE(goldenDataOutputBuffer[i] == mDataOutputBuffer[i]) << "data does not match"; }*/ } mFilterMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED)); return result; } bool FilterCallback::dumpAvData(DemuxFilterMediaEvent event) { uint32_t length = event.dataLength; uint64_t dataId = event.avDataId; // read data from buffer pointed by a handle hidl_handle handle = event.avMemory; int av_fd = handle.getNativeHandle()->data[0]; uint8_t* buffer = static_cast<uint8_t*>( mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, av_fd, 0 /*offset*/)); if (buffer == MAP_FAILED) { ALOGE("[vts] fail to allocate av buffer, errno=%d", errno); return false; } uint8_t output[length + 1]; memcpy(output, buffer, length); // print buffer and check with golden output. EXPECT_TRUE(mFilter->releaseAvHandle(handle, dataId) == Result::SUCCESS); return true; } AssertionResult FilterTests::openFilterInDemux(DemuxFilterType type) { Result status; EXPECT_TRUE(mDemux) << "Test with openDemux first."; // Create demux callback mFilterCallback = new FilterCallback(); // Add filter to the local demux mDemux->openFilter(type, FMQ_SIZE_16M, mFilterCallback, [&](Result result, const sp<IFilter>& filter) { mFilter = filter; status = result; }); if (status == Result::SUCCESS) { mFilterCallback->setFilterEventType(getFilterEventType(type)); } return AssertionResult(status == Result::SUCCESS); } AssertionResult FilterTests::getNewlyOpenedFilterId(uint32_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."; mFilter->getId([&](Result result, uint32_t filterId) { mFilterId = filterId; status = result; }); if (status == Result::SUCCESS) { mFilterCallback->setFilterId(mFilterId); mFilterCallback->setFilterInterface(mFilter); mUsedFilterIds.insert(mUsedFilterIds.end(), mFilterId); mFilters[mFilterId] = mFilter; mFilterCallbacks[mFilterId] = mFilterCallback; filterId = mFilterId; } return AssertionResult(status == Result::SUCCESS); } AssertionResult FilterTests::configFilter(DemuxFilterSettings setting, uint32_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(uint32_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; }); if (status == Result::SUCCESS) { mFilterCallbacks[filterId]->updateFilterMQ(mFilterMQDescriptor); } return AssertionResult(status == Result::SUCCESS); } AssertionResult FilterTests::startFilter(uint32_t filterId) { EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first."; Result status = mFilters[filterId]->start(); return AssertionResult(status == Result::SUCCESS); } AssertionResult FilterTests::stopFilter(uint32_t filterId) { EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first."; Result status = mFilters[filterId]->stop(); return AssertionResult(status == Result::SUCCESS); } AssertionResult FilterTests::closeFilter(uint32_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); } No newline at end of file tv/tuner/1.0/vts/functional/FilterTests.h 0 → 100644 +226 −0 Original line number Diff line number Diff line /* * 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 <VtsHalHidlTargetTestBase.h> #include <VtsHalHidlTargetTestEnvBase.h> #include <android-base/logging.h> #include <android/hardware/tv/tuner/1.0/IFilter.h> #include <android/hardware/tv/tuner/1.0/IFilterCallback.h> #include <android/hardware/tv/tuner/1.0/ITuner.h> #include <android/hardware/tv/tuner/1.0/types.h> #include <fmq/MessageQueue.h> #include <gtest/gtest.h> #include <hidl/HidlSupport.h> #include <hidl/HidlTransportSupport.h> #include <hidl/Status.h> #include <utils/Condition.h> #include <utils/Mutex.h> #include <map> 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::DemuxFilterMediaEvent; using android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings; using android::hardware::tv::tuner::V1_0::DemuxFilterPesEvent; using android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings; using android::hardware::tv::tuner::V1_0::DemuxFilterSectionEvent; using android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings; 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::DemuxQueueNotifyBits; using android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings; 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::ITuner; using android::hardware::tv::tuner::V1_0::Result; using ::testing::AssertionResult; enum FilterEventType : uint8_t { UNDEFINED, SECTION, MEDIA, PES, RECORD, MMTPRECORD, DOWNLOAD, TEMI, }; using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>; using MQDesc = MQDescriptorSync<uint8_t>; const uint32_t FMQ_SIZE_1M = 0x100000; const uint32_t FMQ_SIZE_16M = 0x1000000; #define WAIT_TIMEOUT 3000000000 class FilterCallback : public IFilterCallback { public: virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent) override { android::Mutex::Autolock autoLock(mMsgLock); // Temprarily we treat the first coming back filter data on the matching pid a success // once all of the MQ are cleared, means we got all the expected output mFilterEvent = filterEvent; readFilterEventData(); mPidFilterOutputCount++; // mFilterIdToMQ.erase(filterEvent.filterId); // startFilterEventThread(filterEvent); mMsgCondition.signal(); return Void(); } virtual Return<void> onFilterStatus(const DemuxFilterStatus /*status*/) override { return Void(); } void setFilterId(uint32_t filterId) { mFilterId = filterId; } void setFilterInterface(sp<IFilter> filter) { mFilter = filter; } void setFilterEventType(FilterEventType type) { mFilterEventType = type; } void testFilterDataOutput(); void startFilterEventThread(DemuxFilterEvent event); static void* __threadLoopFilter(void* threadArgs); void filterThreadLoop(DemuxFilterEvent& event); void updateFilterMQ(MQDesc& filterMQDescriptor); void updateGoldenOutputMap(string goldenOutputFile); bool readFilterEventData(); bool dumpAvData(DemuxFilterMediaEvent event); private: struct FilterThreadArgs { FilterCallback* user; DemuxFilterEvent event; }; uint16_t mDataLength = 0; std::vector<uint8_t> mDataOutputBuffer; string mFilterIdToGoldenOutput; uint32_t mFilterId; sp<IFilter> mFilter; FilterEventType mFilterEventType; std::unique_ptr<FilterMQ> mFilterMQ; EventFlag* mFilterMQEventFlag; DemuxFilterEvent mFilterEvent; android::Mutex mMsgLock; android::Mutex mFilterOutputLock; android::Condition mMsgCondition; android::Condition mFilterOutputCondition; pthread_t mFilterThread; int mPidFilterOutputCount = 0; }; class FilterTests { public: void setService(sp<ITuner> tuner) { mService = tuner; } void setDemux(sp<IDemux> demux) { mDemux = demux; } std::map<uint32_t, sp<FilterCallback>> getFilterCallbacks() { return mFilterCallbacks; } AssertionResult openFilterInDemux(DemuxFilterType type); AssertionResult getNewlyOpenedFilterId(uint32_t& filterId); AssertionResult configFilter(DemuxFilterSettings setting, uint32_t filterId); AssertionResult getFilterMQDescriptor(uint32_t filterId); AssertionResult startFilter(uint32_t filterId); AssertionResult stopFilter(uint32_t filterId); AssertionResult closeFilter(uint32_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<ITuner> mService; sp<IFilter> mFilter; sp<IDemux> mDemux; std::map<uint32_t, sp<IFilter>> mFilters; std::map<uint32_t, sp<FilterCallback>> mFilterCallbacks; sp<FilterCallback> mFilterCallback; MQDesc mFilterMQDescriptor; vector<uint32_t> mUsedFilterIds; uint32_t mFilterId = -1; }; No newline at end of file Loading
tv/tuner/1.0/vts/functional/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ cc_test { srcs: [ "VtsHalTvTunerV1_0TargetTest.cpp", "FrontendTests.cpp", "DemuxTests.cpp", "FilterTests.cpp", ], static_libs: [ "android.hardware.tv.tuner@1.0", Loading
tv/tuner/1.0/vts/functional/DemuxTests.cpp 0 → 100644 +41 −0 Original line number Diff line number Diff line /* * 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<IDemux>& demux, uint32_t& demuxId) { Result status; mService->openDemux([&](Result result, uint32_t id, const sp<IDemux>& 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
tv/tuner/1.0/vts/functional/DemuxTests.h 0 → 100644 +57 −0 Original line number Diff line number Diff line /* * 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 <VtsHalHidlTargetTestBase.h> #include <VtsHalHidlTargetTestEnvBase.h> #include <android-base/logging.h> #include <android/hardware/tv/tuner/1.0/IDemux.h> #include <android/hardware/tv/tuner/1.0/ITuner.h> #include <android/hardware/tv/tuner/1.0/types.h> #include <binder/MemoryDealer.h> #include <gtest/gtest.h> #include <hidl/ServiceManagement.h> #include <hidl/Status.h> #include <hidlmemory/FrameworkUtils.h> #include <utils/Condition.h> #include <utils/Mutex.h> #include <map> 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::ITuner; using android::hardware::tv::tuner::V1_0::Result; using ::testing::AssertionResult; class DemuxTests { public: sp<ITuner> mService; void setService(sp<ITuner> tuner) { mService = tuner; } AssertionResult openDemux(sp<IDemux>& 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<IDemux> mDemux; }; No newline at end of file
tv/tuner/1.0/vts/functional/FilterTests.cpp 0 → 100644 +226 −0 Original line number Diff line number Diff line /* * 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" void FilterCallback::startFilterEventThread(DemuxFilterEvent event) { struct FilterThreadArgs* threadArgs = (struct FilterThreadArgs*)malloc(sizeof(struct FilterThreadArgs)); threadArgs->user = this; threadArgs->event = event; pthread_create(&mFilterThread, NULL, __threadLoopFilter, (void*)threadArgs); pthread_setname_np(mFilterThread, "test_playback_input_loop"); } void FilterCallback::testFilterDataOutput() { android::Mutex::Autolock autoLock(mMsgLock); while (mPidFilterOutputCount < 1) { if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) { EXPECT_TRUE(false) << "filter output matching pid does not output within timeout"; return; } } mPidFilterOutputCount = 0; ALOGW("[vts] pass and stop"); } void FilterCallback::updateFilterMQ(MQDesc& filterMQDescriptor) { mFilterMQ = std::make_unique<FilterMQ>(filterMQDescriptor, true /* resetPointers */); EXPECT_TRUE(mFilterMQ); EXPECT_TRUE(EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag) == android::OK); } void FilterCallback::updateGoldenOutputMap(string goldenOutputFile) { mFilterIdToGoldenOutput = goldenOutputFile; } void* FilterCallback::__threadLoopFilter(void* threadArgs) { FilterCallback* const self = static_cast<FilterCallback*>(((struct FilterThreadArgs*)threadArgs)->user); self->filterThreadLoop(((struct FilterThreadArgs*)threadArgs)->event); return 0; } void FilterCallback::filterThreadLoop(DemuxFilterEvent& /* event */) { android::Mutex::Autolock autoLock(mFilterOutputLock); // Read from mFilterMQ[event.filterId] per event and filter type // Assemble to filterOutput[filterId] // check if filterOutput[filterId] matches goldenOutput[filterId] // If match, remove filterId entry from MQ map // end thread } bool FilterCallback::readFilterEventData() { bool result = false; DemuxFilterEvent filterEvent = mFilterEvent; ALOGW("[vts] reading from filter FMQ or buffer %d", mFilterId); // todo separate filter handlers for (int i = 0; i < filterEvent.events.size(); i++) { switch (mFilterEventType) { case FilterEventType::SECTION: mDataLength = filterEvent.events[i].section().dataLength; break; case FilterEventType::PES: mDataLength = filterEvent.events[i].pes().dataLength; break; case FilterEventType::MEDIA: return dumpAvData(filterEvent.events[i].media()); case FilterEventType::RECORD: break; case FilterEventType::MMTPRECORD: break; case FilterEventType::DOWNLOAD: break; default: break; } // EXPECT_TRUE(mDataLength == goldenDataOutputBuffer.size()) << "buffer size does not // match"; mDataOutputBuffer.resize(mDataLength); result = mFilterMQ->read(mDataOutputBuffer.data(), mDataLength); EXPECT_TRUE(result) << "can't read from Filter MQ"; /*for (int i = 0; i < mDataLength; i++) { EXPECT_TRUE(goldenDataOutputBuffer[i] == mDataOutputBuffer[i]) << "data does not match"; }*/ } mFilterMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED)); return result; } bool FilterCallback::dumpAvData(DemuxFilterMediaEvent event) { uint32_t length = event.dataLength; uint64_t dataId = event.avDataId; // read data from buffer pointed by a handle hidl_handle handle = event.avMemory; int av_fd = handle.getNativeHandle()->data[0]; uint8_t* buffer = static_cast<uint8_t*>( mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, av_fd, 0 /*offset*/)); if (buffer == MAP_FAILED) { ALOGE("[vts] fail to allocate av buffer, errno=%d", errno); return false; } uint8_t output[length + 1]; memcpy(output, buffer, length); // print buffer and check with golden output. EXPECT_TRUE(mFilter->releaseAvHandle(handle, dataId) == Result::SUCCESS); return true; } AssertionResult FilterTests::openFilterInDemux(DemuxFilterType type) { Result status; EXPECT_TRUE(mDemux) << "Test with openDemux first."; // Create demux callback mFilterCallback = new FilterCallback(); // Add filter to the local demux mDemux->openFilter(type, FMQ_SIZE_16M, mFilterCallback, [&](Result result, const sp<IFilter>& filter) { mFilter = filter; status = result; }); if (status == Result::SUCCESS) { mFilterCallback->setFilterEventType(getFilterEventType(type)); } return AssertionResult(status == Result::SUCCESS); } AssertionResult FilterTests::getNewlyOpenedFilterId(uint32_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."; mFilter->getId([&](Result result, uint32_t filterId) { mFilterId = filterId; status = result; }); if (status == Result::SUCCESS) { mFilterCallback->setFilterId(mFilterId); mFilterCallback->setFilterInterface(mFilter); mUsedFilterIds.insert(mUsedFilterIds.end(), mFilterId); mFilters[mFilterId] = mFilter; mFilterCallbacks[mFilterId] = mFilterCallback; filterId = mFilterId; } return AssertionResult(status == Result::SUCCESS); } AssertionResult FilterTests::configFilter(DemuxFilterSettings setting, uint32_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(uint32_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; }); if (status == Result::SUCCESS) { mFilterCallbacks[filterId]->updateFilterMQ(mFilterMQDescriptor); } return AssertionResult(status == Result::SUCCESS); } AssertionResult FilterTests::startFilter(uint32_t filterId) { EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first."; Result status = mFilters[filterId]->start(); return AssertionResult(status == Result::SUCCESS); } AssertionResult FilterTests::stopFilter(uint32_t filterId) { EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first."; Result status = mFilters[filterId]->stop(); return AssertionResult(status == Result::SUCCESS); } AssertionResult FilterTests::closeFilter(uint32_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); } No newline at end of file
tv/tuner/1.0/vts/functional/FilterTests.h 0 → 100644 +226 −0 Original line number Diff line number Diff line /* * 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 <VtsHalHidlTargetTestBase.h> #include <VtsHalHidlTargetTestEnvBase.h> #include <android-base/logging.h> #include <android/hardware/tv/tuner/1.0/IFilter.h> #include <android/hardware/tv/tuner/1.0/IFilterCallback.h> #include <android/hardware/tv/tuner/1.0/ITuner.h> #include <android/hardware/tv/tuner/1.0/types.h> #include <fmq/MessageQueue.h> #include <gtest/gtest.h> #include <hidl/HidlSupport.h> #include <hidl/HidlTransportSupport.h> #include <hidl/Status.h> #include <utils/Condition.h> #include <utils/Mutex.h> #include <map> 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::DemuxFilterMediaEvent; using android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings; using android::hardware::tv::tuner::V1_0::DemuxFilterPesEvent; using android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings; using android::hardware::tv::tuner::V1_0::DemuxFilterSectionEvent; using android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings; 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::DemuxQueueNotifyBits; using android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings; 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::ITuner; using android::hardware::tv::tuner::V1_0::Result; using ::testing::AssertionResult; enum FilterEventType : uint8_t { UNDEFINED, SECTION, MEDIA, PES, RECORD, MMTPRECORD, DOWNLOAD, TEMI, }; using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>; using MQDesc = MQDescriptorSync<uint8_t>; const uint32_t FMQ_SIZE_1M = 0x100000; const uint32_t FMQ_SIZE_16M = 0x1000000; #define WAIT_TIMEOUT 3000000000 class FilterCallback : public IFilterCallback { public: virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent) override { android::Mutex::Autolock autoLock(mMsgLock); // Temprarily we treat the first coming back filter data on the matching pid a success // once all of the MQ are cleared, means we got all the expected output mFilterEvent = filterEvent; readFilterEventData(); mPidFilterOutputCount++; // mFilterIdToMQ.erase(filterEvent.filterId); // startFilterEventThread(filterEvent); mMsgCondition.signal(); return Void(); } virtual Return<void> onFilterStatus(const DemuxFilterStatus /*status*/) override { return Void(); } void setFilterId(uint32_t filterId) { mFilterId = filterId; } void setFilterInterface(sp<IFilter> filter) { mFilter = filter; } void setFilterEventType(FilterEventType type) { mFilterEventType = type; } void testFilterDataOutput(); void startFilterEventThread(DemuxFilterEvent event); static void* __threadLoopFilter(void* threadArgs); void filterThreadLoop(DemuxFilterEvent& event); void updateFilterMQ(MQDesc& filterMQDescriptor); void updateGoldenOutputMap(string goldenOutputFile); bool readFilterEventData(); bool dumpAvData(DemuxFilterMediaEvent event); private: struct FilterThreadArgs { FilterCallback* user; DemuxFilterEvent event; }; uint16_t mDataLength = 0; std::vector<uint8_t> mDataOutputBuffer; string mFilterIdToGoldenOutput; uint32_t mFilterId; sp<IFilter> mFilter; FilterEventType mFilterEventType; std::unique_ptr<FilterMQ> mFilterMQ; EventFlag* mFilterMQEventFlag; DemuxFilterEvent mFilterEvent; android::Mutex mMsgLock; android::Mutex mFilterOutputLock; android::Condition mMsgCondition; android::Condition mFilterOutputCondition; pthread_t mFilterThread; int mPidFilterOutputCount = 0; }; class FilterTests { public: void setService(sp<ITuner> tuner) { mService = tuner; } void setDemux(sp<IDemux> demux) { mDemux = demux; } std::map<uint32_t, sp<FilterCallback>> getFilterCallbacks() { return mFilterCallbacks; } AssertionResult openFilterInDemux(DemuxFilterType type); AssertionResult getNewlyOpenedFilterId(uint32_t& filterId); AssertionResult configFilter(DemuxFilterSettings setting, uint32_t filterId); AssertionResult getFilterMQDescriptor(uint32_t filterId); AssertionResult startFilter(uint32_t filterId); AssertionResult stopFilter(uint32_t filterId); AssertionResult closeFilter(uint32_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<ITuner> mService; sp<IFilter> mFilter; sp<IDemux> mDemux; std::map<uint32_t, sp<IFilter>> mFilters; std::map<uint32_t, sp<FilterCallback>> mFilterCallbacks; sp<FilterCallback> mFilterCallback; MQDesc mFilterMQDescriptor; vector<uint32_t> mUsedFilterIds; uint32_t mFilterId = -1; }; No newline at end of file