Loading tv/tuner/aidl/vts/functional/FilterTests.cpp +14 −12 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include "FilterTests.h" #include <inttypes.h> #include <algorithm> #include <aidl/android/hardware/tv/tuner/DemuxFilterMonitorEventType.h> #include <aidlcommonsupport/NativeHandle.h> Loading @@ -31,23 +32,24 @@ using ::aidl::android::hardware::common::NativeHandle; mPidFilterOutputCount++; mMsgCondition.signal(); // HACK: we need to cast the const away as DemuxFilterEvent contains a ScopedFileDescriptor // that cannot be copied. for (auto&& e : const_cast<std::vector<DemuxFilterEvent>&>(events)) { auto it = mFilterEventPromises.find(e.getTag()); if (it != mFilterEventPromises.cend()) { it->second.set_value(std::move(e)); mFilterEventPromises.erase(it); } for (auto it = mFilterCallbackVerifiers.begin(); it != mFilterCallbackVerifiers.end();) { auto& [verifier, promise] = *it; if (verifier(events)) { promise.set_value(); it = mFilterCallbackVerifiers.erase(it); } else { ++it; } }; return ::ndk::ScopedAStatus::ok(); } std::future<DemuxFilterEvent> FilterCallback::getNextFilterEventWithTag(DemuxFilterEvent::Tag tag) { // Note: this currently only supports one future per DemuxFilterEvent::Tag. mFilterEventPromises[tag] = std::promise<DemuxFilterEvent>(); return mFilterEventPromises[tag].get_future(); std::future<void> FilterCallback::verifyFilterCallback(FilterCallbackVerifier&& verifier) { std::promise<void> promise; auto future = promise.get_future(); mFilterCallbackVerifiers.emplace_back(std::move(verifier), std::move(promise)); return future; } void FilterCallback::testFilterDataOutput() { Loading tv/tuner/aidl/vts/functional/FilterTests.h +11 −2 Original line number Diff line number Diff line Loading @@ -60,9 +60,18 @@ using MQDesc = MQDescriptor<int8_t, SynchronizedReadWrite>; class FilterCallback : public BnFilterCallback { public: /** * A FilterCallbackVerifier is used to test and verify filter callbacks. * The function should return true when a callback has been handled by this * filter verifier. This will cause the associated future to be unblocked. * If the function returns false, we continue to wait for future callbacks * (the future remains blocked). */ using FilterCallbackVerifier = std::function<bool(const std::vector<DemuxFilterEvent>&)>; virtual ::ndk::ScopedAStatus onFilterEvent(const vector<DemuxFilterEvent>& events) override; std::future<DemuxFilterEvent> getNextFilterEventWithTag(DemuxFilterEvent::Tag tag); std::future<void> verifyFilterCallback(FilterCallbackVerifier&& verifier); virtual ::ndk::ScopedAStatus onFilterStatus(const DemuxFilterStatus /*status*/) override { return ::ndk::ScopedAStatus::ok(); Loading @@ -85,7 +94,7 @@ class FilterCallback : public BnFilterCallback { int32_t mFilterId; std::shared_ptr<IFilter> mFilter; std::unordered_map<DemuxFilterEvent::Tag, std::promise<DemuxFilterEvent>> mFilterEventPromises; std::vector<std::pair<FilterCallbackVerifier, std::promise<void>>> mFilterCallbackVerifiers; native_handle_t* mAvSharedHandle = nullptr; uint64_t mAvSharedMemSize = -1; Loading tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp +113 −25 Original line number Diff line number Diff line Loading @@ -640,10 +640,51 @@ TEST_P(TunerFilterAidlTest, testTimeFilter) { testTimeFilter(timeFilterMap[timeFilter.timeFilterId]); } // TODO: move boilerplate into text fixture TEST_P(TunerFilterAidlTest, FilterTimeDelayHintTest) { description("Test filter delay hint."); static bool isMediaFilter(const FilterConfig& filterConfig) { switch (filterConfig.type.mainType) { case DemuxFilterMainType::TS: { // TS Audio and Video filters are media filters auto tsFilterType = filterConfig.type.subType.get<DemuxFilterSubType::Tag::tsFilterType>(); return (tsFilterType == DemuxTsFilterType::AUDIO || tsFilterType == DemuxTsFilterType::VIDEO); } case DemuxFilterMainType::MMTP: { // MMTP Audio and Video filters are media filters auto mmtpFilterType = filterConfig.type.subType.get<DemuxFilterSubType::Tag::mmtpFilterType>(); return (mmtpFilterType == DemuxMmtpFilterType::AUDIO || mmtpFilterType == DemuxMmtpFilterType::VIDEO); } default: return false; } } static int getDemuxFilterEventDataLength(const DemuxFilterEvent& event) { switch (event.getTag()) { case DemuxFilterEvent::Tag::section: return event.get<DemuxFilterEvent::Tag::section>().dataLength; case DemuxFilterEvent::Tag::media: return event.get<DemuxFilterEvent::Tag::media>().dataLength; case DemuxFilterEvent::Tag::pes: return event.get<DemuxFilterEvent::Tag::pes>().dataLength; case DemuxFilterEvent::Tag::download: return event.get<DemuxFilterEvent::Tag::download>().dataLength; case DemuxFilterEvent::Tag::ipPayload: return event.get<DemuxFilterEvent::Tag::ipPayload>().dataLength; case DemuxFilterEvent::Tag::tsRecord: case DemuxFilterEvent::Tag::mmtpRecord: case DemuxFilterEvent::Tag::temi: case DemuxFilterEvent::Tag::monitorEvent: case DemuxFilterEvent::Tag::startId: return 0; } } // TODO: move boilerplate into text fixture void TunerFilterAidlTest::testDelayHint(const FilterConfig& filterConf) { int32_t feId; int32_t demuxId; std::shared_ptr<IDemux> demux; Loading @@ -657,40 +698,87 @@ TEST_P(TunerFilterAidlTest, FilterTimeDelayHintTest) { ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId)); mFilterTests.setDemux(demux); const auto& filterConf = filterMap[live.ipFilterId]; ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize)); ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId)); ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId)); ASSERT_TRUE(mFilterTests.configIpFilterCid(filterConf.ipCid, filterId)); bool mediaFilter = isMediaFilter(filterConf); auto filter = mFilterTests.getFilterById(filterId); auto delayValue = std::chrono::milliseconds(5000); auto timeDelayInMs = std::chrono::milliseconds(filterConf.timeDelayInMs); if (timeDelayInMs.count() > 0) { FilterDelayHint delayHint; delayHint.hintType = FilterDelayHintType::TIME_DELAY_IN_MS; delayHint.hintValue = delayValue.count(); delayHint.hintValue = timeDelayInMs.count(); auto status = filter->setDelayHint(delayHint); ASSERT_TRUE(status.isOk()); // setDelayHint should fail for media filters. ASSERT_EQ(filter->setDelayHint(delayHint).isOk(), !mediaFilter); } auto startTime = std::chrono::steady_clock::now(); ASSERT_TRUE(mFilterTests.startFilter(filterId)); int dataDelayInBytes = filterConf.dataDelayInBytes; if (dataDelayInBytes > 0) { FilterDelayHint delayHint; delayHint.hintType = FilterDelayHintType::DATA_SIZE_DELAY_IN_BYTES; delayHint.hintValue = dataDelayInBytes; // setDelayHint should fail for media filters. ASSERT_EQ(filter->setDelayHint(delayHint).isOk(), !mediaFilter); } // start and stop filter in order to circumvent callback scheduler race // conditions after adjusting filter delays. mFilterTests.startFilter(filterId); mFilterTests.stopFilter(filterId); if (!mediaFilter) { auto cb = mFilterTests.getFilterCallbacks().at(filterId); auto future = cb->getNextFilterEventWithTag(DemuxFilterEvent::Tag::ipPayload); int callbackSize = 0; auto future = cb->verifyFilterCallback( [&callbackSize](const std::vector<DemuxFilterEvent>& events) { for (const auto& event : events) { callbackSize += getDemuxFilterEventDataLength(event); } return true; }); // The configure stage can also produce events, so we should set the delay // hint beforehand. ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId)); auto startTime = std::chrono::steady_clock::now(); ASSERT_TRUE(mFilterTests.startFilter(filterId)); // block and wait for callback to be received. ASSERT_EQ(future.wait_for(std::chrono::seconds(10)), std::future_status::ready); auto timeout = std::chrono::seconds(30); ASSERT_EQ(future.wait_for(timeout), std::future_status::ready); auto duration = std::chrono::steady_clock::now() - startTime; ASSERT_GE(duration, delayValue); // cleanup bool delayHintTest = duration >= timeDelayInMs; bool dataSizeTest = callbackSize >= dataDelayInBytes; if (timeDelayInMs.count() > 0 && dataDelayInBytes > 0) { ASSERT_TRUE(delayHintTest || dataSizeTest); } else { // if only one of time delay / data delay is configured, one of them // holds true by default, so we want both assertions to be true. ASSERT_TRUE(delayHintTest && dataSizeTest); } ASSERT_TRUE(mFilterTests.stopFilter(filterId)); } ASSERT_TRUE(mFilterTests.closeFilter(filterId)); ASSERT_TRUE(mDemuxTests.closeDemux()); ASSERT_TRUE(mFrontendTests.closeFrontend()); } TEST_P(TunerFilterAidlTest, FilterDelayHintTest) { description("Test filter time delay hint."); for (const auto& obj : filterMap) { testDelayHint(obj.second); } } TEST_P(TunerPlaybackAidlTest, PlaybackDataFlowWithTsSectionFilterTest) { description("Feed ts data from playback and configure Ts section filter to get output"); if (!playback.support || playback.sectionFilterId.compare(emptyHardwareId) == 0) { Loading tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h +1 −0 Original line number Diff line number Diff line Loading @@ -140,6 +140,7 @@ class TunerFilterAidlTest : public testing::TestWithParam<std::string> { void reconfigSingleFilterInDemuxTest(FilterConfig filterConf, FilterConfig filterReconf, FrontendConfig frontendConf); void testTimeFilter(TimeFilterConfig filterConf); void testDelayHint(const FilterConfig& filterConf); DemuxFilterType getLinkageFilterType(int bit) { DemuxFilterType type; Loading tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h +8 −0 Original line number Diff line number Diff line Loading @@ -96,6 +96,8 @@ struct FilterConfig { AvStreamType streamType; int32_t ipCid; int32_t monitorEventTypes; int timeDelayInMs = 0; int dataDelayInBytes = 0; bool operator<(const FilterConfig& /*c*/) const { return false; } }; Loading Loading @@ -336,6 +338,12 @@ struct TunerTestingConfigAidlReader1_0 { if (filterConfig.hasMonitorEventTypes()) { filterMap[id].monitorEventTypes = (int32_t)filterConfig.getMonitorEventTypes(); } if (filterConfig.hasTimeDelayInMs()) { filterMap[id].timeDelayInMs = filterConfig.getTimeDelayInMs(); } if (filterConfig.hasDataDelayInBytes()) { filterMap[id].dataDelayInBytes = filterConfig.getDataDelayInBytes(); } if (filterConfig.hasAvFilterSettings_optional()) { auto av = filterConfig.getFirstAvFilterSettings_optional(); if (av->hasAudioStreamType_optional()) { Loading Loading
tv/tuner/aidl/vts/functional/FilterTests.cpp +14 −12 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include "FilterTests.h" #include <inttypes.h> #include <algorithm> #include <aidl/android/hardware/tv/tuner/DemuxFilterMonitorEventType.h> #include <aidlcommonsupport/NativeHandle.h> Loading @@ -31,23 +32,24 @@ using ::aidl::android::hardware::common::NativeHandle; mPidFilterOutputCount++; mMsgCondition.signal(); // HACK: we need to cast the const away as DemuxFilterEvent contains a ScopedFileDescriptor // that cannot be copied. for (auto&& e : const_cast<std::vector<DemuxFilterEvent>&>(events)) { auto it = mFilterEventPromises.find(e.getTag()); if (it != mFilterEventPromises.cend()) { it->second.set_value(std::move(e)); mFilterEventPromises.erase(it); } for (auto it = mFilterCallbackVerifiers.begin(); it != mFilterCallbackVerifiers.end();) { auto& [verifier, promise] = *it; if (verifier(events)) { promise.set_value(); it = mFilterCallbackVerifiers.erase(it); } else { ++it; } }; return ::ndk::ScopedAStatus::ok(); } std::future<DemuxFilterEvent> FilterCallback::getNextFilterEventWithTag(DemuxFilterEvent::Tag tag) { // Note: this currently only supports one future per DemuxFilterEvent::Tag. mFilterEventPromises[tag] = std::promise<DemuxFilterEvent>(); return mFilterEventPromises[tag].get_future(); std::future<void> FilterCallback::verifyFilterCallback(FilterCallbackVerifier&& verifier) { std::promise<void> promise; auto future = promise.get_future(); mFilterCallbackVerifiers.emplace_back(std::move(verifier), std::move(promise)); return future; } void FilterCallback::testFilterDataOutput() { Loading
tv/tuner/aidl/vts/functional/FilterTests.h +11 −2 Original line number Diff line number Diff line Loading @@ -60,9 +60,18 @@ using MQDesc = MQDescriptor<int8_t, SynchronizedReadWrite>; class FilterCallback : public BnFilterCallback { public: /** * A FilterCallbackVerifier is used to test and verify filter callbacks. * The function should return true when a callback has been handled by this * filter verifier. This will cause the associated future to be unblocked. * If the function returns false, we continue to wait for future callbacks * (the future remains blocked). */ using FilterCallbackVerifier = std::function<bool(const std::vector<DemuxFilterEvent>&)>; virtual ::ndk::ScopedAStatus onFilterEvent(const vector<DemuxFilterEvent>& events) override; std::future<DemuxFilterEvent> getNextFilterEventWithTag(DemuxFilterEvent::Tag tag); std::future<void> verifyFilterCallback(FilterCallbackVerifier&& verifier); virtual ::ndk::ScopedAStatus onFilterStatus(const DemuxFilterStatus /*status*/) override { return ::ndk::ScopedAStatus::ok(); Loading @@ -85,7 +94,7 @@ class FilterCallback : public BnFilterCallback { int32_t mFilterId; std::shared_ptr<IFilter> mFilter; std::unordered_map<DemuxFilterEvent::Tag, std::promise<DemuxFilterEvent>> mFilterEventPromises; std::vector<std::pair<FilterCallbackVerifier, std::promise<void>>> mFilterCallbackVerifiers; native_handle_t* mAvSharedHandle = nullptr; uint64_t mAvSharedMemSize = -1; Loading
tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp +113 −25 Original line number Diff line number Diff line Loading @@ -640,10 +640,51 @@ TEST_P(TunerFilterAidlTest, testTimeFilter) { testTimeFilter(timeFilterMap[timeFilter.timeFilterId]); } // TODO: move boilerplate into text fixture TEST_P(TunerFilterAidlTest, FilterTimeDelayHintTest) { description("Test filter delay hint."); static bool isMediaFilter(const FilterConfig& filterConfig) { switch (filterConfig.type.mainType) { case DemuxFilterMainType::TS: { // TS Audio and Video filters are media filters auto tsFilterType = filterConfig.type.subType.get<DemuxFilterSubType::Tag::tsFilterType>(); return (tsFilterType == DemuxTsFilterType::AUDIO || tsFilterType == DemuxTsFilterType::VIDEO); } case DemuxFilterMainType::MMTP: { // MMTP Audio and Video filters are media filters auto mmtpFilterType = filterConfig.type.subType.get<DemuxFilterSubType::Tag::mmtpFilterType>(); return (mmtpFilterType == DemuxMmtpFilterType::AUDIO || mmtpFilterType == DemuxMmtpFilterType::VIDEO); } default: return false; } } static int getDemuxFilterEventDataLength(const DemuxFilterEvent& event) { switch (event.getTag()) { case DemuxFilterEvent::Tag::section: return event.get<DemuxFilterEvent::Tag::section>().dataLength; case DemuxFilterEvent::Tag::media: return event.get<DemuxFilterEvent::Tag::media>().dataLength; case DemuxFilterEvent::Tag::pes: return event.get<DemuxFilterEvent::Tag::pes>().dataLength; case DemuxFilterEvent::Tag::download: return event.get<DemuxFilterEvent::Tag::download>().dataLength; case DemuxFilterEvent::Tag::ipPayload: return event.get<DemuxFilterEvent::Tag::ipPayload>().dataLength; case DemuxFilterEvent::Tag::tsRecord: case DemuxFilterEvent::Tag::mmtpRecord: case DemuxFilterEvent::Tag::temi: case DemuxFilterEvent::Tag::monitorEvent: case DemuxFilterEvent::Tag::startId: return 0; } } // TODO: move boilerplate into text fixture void TunerFilterAidlTest::testDelayHint(const FilterConfig& filterConf) { int32_t feId; int32_t demuxId; std::shared_ptr<IDemux> demux; Loading @@ -657,40 +698,87 @@ TEST_P(TunerFilterAidlTest, FilterTimeDelayHintTest) { ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId)); mFilterTests.setDemux(demux); const auto& filterConf = filterMap[live.ipFilterId]; ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize)); ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId)); ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId)); ASSERT_TRUE(mFilterTests.configIpFilterCid(filterConf.ipCid, filterId)); bool mediaFilter = isMediaFilter(filterConf); auto filter = mFilterTests.getFilterById(filterId); auto delayValue = std::chrono::milliseconds(5000); auto timeDelayInMs = std::chrono::milliseconds(filterConf.timeDelayInMs); if (timeDelayInMs.count() > 0) { FilterDelayHint delayHint; delayHint.hintType = FilterDelayHintType::TIME_DELAY_IN_MS; delayHint.hintValue = delayValue.count(); delayHint.hintValue = timeDelayInMs.count(); auto status = filter->setDelayHint(delayHint); ASSERT_TRUE(status.isOk()); // setDelayHint should fail for media filters. ASSERT_EQ(filter->setDelayHint(delayHint).isOk(), !mediaFilter); } auto startTime = std::chrono::steady_clock::now(); ASSERT_TRUE(mFilterTests.startFilter(filterId)); int dataDelayInBytes = filterConf.dataDelayInBytes; if (dataDelayInBytes > 0) { FilterDelayHint delayHint; delayHint.hintType = FilterDelayHintType::DATA_SIZE_DELAY_IN_BYTES; delayHint.hintValue = dataDelayInBytes; // setDelayHint should fail for media filters. ASSERT_EQ(filter->setDelayHint(delayHint).isOk(), !mediaFilter); } // start and stop filter in order to circumvent callback scheduler race // conditions after adjusting filter delays. mFilterTests.startFilter(filterId); mFilterTests.stopFilter(filterId); if (!mediaFilter) { auto cb = mFilterTests.getFilterCallbacks().at(filterId); auto future = cb->getNextFilterEventWithTag(DemuxFilterEvent::Tag::ipPayload); int callbackSize = 0; auto future = cb->verifyFilterCallback( [&callbackSize](const std::vector<DemuxFilterEvent>& events) { for (const auto& event : events) { callbackSize += getDemuxFilterEventDataLength(event); } return true; }); // The configure stage can also produce events, so we should set the delay // hint beforehand. ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId)); auto startTime = std::chrono::steady_clock::now(); ASSERT_TRUE(mFilterTests.startFilter(filterId)); // block and wait for callback to be received. ASSERT_EQ(future.wait_for(std::chrono::seconds(10)), std::future_status::ready); auto timeout = std::chrono::seconds(30); ASSERT_EQ(future.wait_for(timeout), std::future_status::ready); auto duration = std::chrono::steady_clock::now() - startTime; ASSERT_GE(duration, delayValue); // cleanup bool delayHintTest = duration >= timeDelayInMs; bool dataSizeTest = callbackSize >= dataDelayInBytes; if (timeDelayInMs.count() > 0 && dataDelayInBytes > 0) { ASSERT_TRUE(delayHintTest || dataSizeTest); } else { // if only one of time delay / data delay is configured, one of them // holds true by default, so we want both assertions to be true. ASSERT_TRUE(delayHintTest && dataSizeTest); } ASSERT_TRUE(mFilterTests.stopFilter(filterId)); } ASSERT_TRUE(mFilterTests.closeFilter(filterId)); ASSERT_TRUE(mDemuxTests.closeDemux()); ASSERT_TRUE(mFrontendTests.closeFrontend()); } TEST_P(TunerFilterAidlTest, FilterDelayHintTest) { description("Test filter time delay hint."); for (const auto& obj : filterMap) { testDelayHint(obj.second); } } TEST_P(TunerPlaybackAidlTest, PlaybackDataFlowWithTsSectionFilterTest) { description("Feed ts data from playback and configure Ts section filter to get output"); if (!playback.support || playback.sectionFilterId.compare(emptyHardwareId) == 0) { Loading
tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h +1 −0 Original line number Diff line number Diff line Loading @@ -140,6 +140,7 @@ class TunerFilterAidlTest : public testing::TestWithParam<std::string> { void reconfigSingleFilterInDemuxTest(FilterConfig filterConf, FilterConfig filterReconf, FrontendConfig frontendConf); void testTimeFilter(TimeFilterConfig filterConf); void testDelayHint(const FilterConfig& filterConf); DemuxFilterType getLinkageFilterType(int bit) { DemuxFilterType type; Loading
tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h +8 −0 Original line number Diff line number Diff line Loading @@ -96,6 +96,8 @@ struct FilterConfig { AvStreamType streamType; int32_t ipCid; int32_t monitorEventTypes; int timeDelayInMs = 0; int dataDelayInBytes = 0; bool operator<(const FilterConfig& /*c*/) const { return false; } }; Loading Loading @@ -336,6 +338,12 @@ struct TunerTestingConfigAidlReader1_0 { if (filterConfig.hasMonitorEventTypes()) { filterMap[id].monitorEventTypes = (int32_t)filterConfig.getMonitorEventTypes(); } if (filterConfig.hasTimeDelayInMs()) { filterMap[id].timeDelayInMs = filterConfig.getTimeDelayInMs(); } if (filterConfig.hasDataDelayInBytes()) { filterMap[id].dataDelayInBytes = filterConfig.getDataDelayInBytes(); } if (filterConfig.hasAvFilterSettings_optional()) { auto av = filterConfig.getFirstAvFilterSettings_optional(); if (av->hasAudioStreamType_optional()) { Loading