diff --git a/tv/tuner/1.0/default/Android.bp b/tv/tuner/1.0/default/Android.bp index 989e25c6e3..5711889aa4 100644 --- a/tv/tuner/1.0/default/Android.bp +++ b/tv/tuner/1.0/default/Android.bp @@ -24,6 +24,7 @@ cc_defaults { "libfmq", "libhidlbase", "libhidlmemory", + "libion", "liblog", "libstagefright_foundation", "libutils", diff --git a/tv/tuner/1.0/default/Filter.cpp b/tv/tuner/1.0/default/Filter.cpp index 54d09520e0..f610c60743 100644 --- a/tv/tuner/1.0/default/Filter.cpp +++ b/tv/tuner/1.0/default/Filter.cpp @@ -60,6 +60,8 @@ Return Filter::setDataSource(const sp& filter) { Return Filter::getQueueDesc(getQueueDesc_cb _hidl_cb) { ALOGV("%s", __FUNCTION__); + mIsUsingFMQ = true; + _hidl_cb(Result::SUCCESS, *mFilterMQ->getDesc()); return Void(); } @@ -120,9 +122,13 @@ Return Filter::flush() { return Result::SUCCESS; } -Return Filter::releaseAvHandle(const hidl_handle& /*avMemory*/, uint64_t /*avDataId*/) { +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; } @@ -174,14 +180,21 @@ void Filter::filterThreadLoop() { // Event Callback without waiting for the DATA_CONSUMED to init the process. while (mFilterThreadRunning) { if (mFilterEvent.events.size() == 0) { - ALOGD("[Filter] wait for filter data output."); + 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 %d does not hava callback. Ending thread", mFilterId); + break; + } mCallback->onFilterStatus(mFilterStatus); break; } @@ -191,7 +204,7 @@ void Filter::filterThreadLoop() { // 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) { + while (mFilterThreadRunning && mIsUsingFMQ) { status_t status = mFilterEventFlag->wait( static_cast(DemuxQueueNotifyBits::DATA_CONSUMED), &efState, WAIT_TIMEOUT, true /* retry on spurious wake */); @@ -202,11 +215,6 @@ void Filter::filterThreadLoop() { break; } - if (mCallback == nullptr) { - ALOGD("[Filter] filter %d does not hava callback. Ending thread", mFilterId); - break; - } - maySendFilterStatusCallback(); while (mFilterThreadRunning) { @@ -232,7 +240,22 @@ void Filter::filterThreadLoop() { ALOGD("[Filter] filter thread ended."); } +void Filter::freeAvHandle() { + if (mType.mainType != DemuxFilterMainType::TS || + (mType.subType.tsFilterType() == DemuxTsFilterType::AUDIO && + mType.subType.tsFilterType() == DemuxTsFilterType::VIDEO)) { + 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(); @@ -409,19 +432,88 @@ Result Filter::startTsFilterHandler() { } Result Filter::startMediaFilterHandler() { - DemuxFilterMediaEvent mediaEvent; - mediaEvent = { - // temp dump meta data - .pts = 0, - .dataLength = 530, - .avMemory = nullptr, - .isSecureMemory = false, - }; - mFilterEvent.events.resize(1); - mFilterEvent.events[0].media(mediaEvent); + 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 || mAvBufferCopyCount++ < 10) { + continue; + } + + int av_fd = createAvIonFd(mPesOutput.size()); + if (av_fd == -1) { + return Result::UNKNOWN_ERROR; + } + // copy the filtered data to the buffer + uint8_t* avBuffer = getIonBuffer(av_fd, mPesOutput.size()); + if (avBuffer == NULL) { + return Result::UNKNOWN_ERROR; + } + memcpy(avBuffer, mPesOutput.data(), mPesOutput.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(mPesOutput.size()), + .avDataId = dataId, + }; + int size = mFilterEvent.events.size(); + mFilterEvent.events.resize(size + 1); + mFilterEvent.events[size].media(mediaEvent); + + // Clear and log + mPesOutput.clear(); + mAvBufferCopyCount = 0; + ::close(av_fd); + if (DEBUG_FILTER) { + ALOGD("[Filter] assembled av data length %d", mediaEvent.dataLength); + } + } mFilterOutput.clear(); - // TODO handle write FQM for media stream + return Result::SUCCESS; } @@ -493,6 +585,42 @@ 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 diff --git a/tv/tuner/1.0/default/Filter.h b/tv/tuner/1.0/default/Filter.h index 0dc992a2ba..afed98eb0a 100644 --- a/tv/tuner/1.0/default/Filter.h +++ b/tv/tuner/1.0/default/Filter.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include "Demux.h" @@ -87,6 +88,7 @@ class Filter : public IFilter { Result startRecordFilterHandler(); void attachFilterToRecord(const sp dvr); void detachFilterFromRecord(); + void freeAvHandle(); private: // Tuner service @@ -109,6 +111,7 @@ class Filter : public IFilter { vector mFilterOutput; vector mRecordFilterOutput; unique_ptr mFilterMQ; + bool mIsUsingFMQ = false; EventFlag* mFilterEventFlag; DemuxFilterEvent mFilterEvent; @@ -160,6 +163,10 @@ class Filter : public IFilter { static void* __threadLoopFilter(void* user); void filterThreadLoop(); + int createAvIonFd(int size); + uint8_t* getIonBuffer(int fd, int size); + native_handle_t* createNativeHandle(int fd); + /** * Lock to protect writes to the FMQs */ @@ -181,6 +188,11 @@ class Filter : public IFilter { // 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 diff --git a/tv/tuner/1.0/default/Frontend.cpp b/tv/tuner/1.0/default/Frontend.cpp index 7e206a76cb..bb0d8dcaf9 100644 --- a/tv/tuner/1.0/default/Frontend.cpp +++ b/tv/tuner/1.0/default/Frontend.cpp @@ -63,9 +63,6 @@ Return Frontend::tune(const FrontendSettings& /* settings */) { return Result::INVALID_STATE; } - // TODO dynamically allocate file to the source file - mSourceStreamFile = FRONTEND_STREAM_FILE; - mCallback->onEvent(FrontendEventType::LOCKED); return Result::SUCCESS; } @@ -180,7 +177,7 @@ FrontendId Frontend::getFrontendId() { } string Frontend::getSourceFile() { - return mSourceStreamFile; + return FRONTEND_STREAM_FILE; } } // namespace implementation diff --git a/tv/tuner/1.0/default/Frontend.h b/tv/tuner/1.0/default/Frontend.h index eab43a39fb..b954639956 100644 --- a/tv/tuner/1.0/default/Frontend.h +++ b/tv/tuner/1.0/default/Frontend.h @@ -76,7 +76,6 @@ class Frontend : public IFrontend { FrontendId mId = 0; const string FRONTEND_STREAM_FILE = "/vendor/etc/dumpTs3.ts"; - string mSourceStreamFile; std::ifstream mFrontendData; }; diff --git a/tv/tuner/1.0/default/Tuner.cpp b/tv/tuner/1.0/default/Tuner.cpp index 4fd33559cc..8fb506132a 100644 --- a/tv/tuner/1.0/default/Tuner.cpp +++ b/tv/tuner/1.0/default/Tuner.cpp @@ -106,7 +106,7 @@ Return Tuner::openDescrambler(openDescrambler_cb _hidl_cb) { return Void(); } -Return Tuner::getFrontendInfo(FrontendId /* frontendId */, getFrontendInfo_cb _hidl_cb) { +Return Tuner::getFrontendInfo(FrontendId /*frontendId*/, getFrontendInfo_cb _hidl_cb) { ALOGV("%s", __FUNCTION__); vector statusCaps = { diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp index f693e7cff9..8b0413cec8 100644 --- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp +++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp @@ -47,7 +47,6 @@ #include "VtsHalTvTunerV1_0TestConfigurations.h" #define WAIT_TIMEOUT 3000000000 -#define WAIT_TIMEOUT_data_ready 3000000000 * 4 using android::Condition; using android::IMemory; @@ -57,6 +56,7 @@ using android::Mutex; using android::sp; using android::hardware::EventFlag; using android::hardware::fromHeap; +using android::hardware::hidl_handle; using android::hardware::hidl_string; using android::hardware::hidl_vec; using android::hardware::HidlMemory; @@ -68,6 +68,7 @@ using android::hardware::Void; using android::hardware::tv::tuner::V1_0::DataFormat; 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; @@ -156,7 +157,6 @@ const std::vector goldenDataOutputBuffer{ // const uint16_t FMQ_SIZE_4K = 0x1000; const uint32_t FMQ_SIZE_1M = 0x100000; const uint32_t FMQ_SIZE_16M = 0x1000000; -const uint8_t FRONTEND_EVENT_CALLBACK_WAIT_COUNT = 4; enum FilterEventType : uint8_t { UNDEFINED, @@ -179,52 +179,65 @@ class FrontendCallback : public IFrontendCallback { public: virtual Return onEvent(FrontendEventType frontendEventType) override { android::Mutex::Autolock autoLock(mMsgLock); + ALOGD("[vts] frontend event received. Type: %d", frontendEventType); mEventReceived = true; - mEventType = frontendEventType; mMsgCondition.signal(); - return Void(); + switch (frontendEventType) { + case FrontendEventType::LOCKED: + mLockMsgReceived = true; + mLockMsgCondition.signal(); + return Void(); + default: + // do nothing + return Void(); + } } virtual Return onScanMessage(FrontendScanMessageType type, const FrontendScanMessage& message) override { android::Mutex::Autolock autoLock(mMsgLock); - ALOGD("[vts] scan message. Type: %d", mScanMessageType); + while (!mScanMsgProcessed) { + mMsgCondition.wait(mMsgLock); + } + ALOGD("[vts] frontend scan message. Type: %d", type); mScanMessageReceived = true; + mScanMsgProcessed = false; mScanMessageType = type; - mScanLockMessageReceived = - mScanLockMessageReceived | (type == FrontendScanMessageType::LOCKED); mScanMessage = message; mMsgCondition.signal(); return Void(); - }; + } void tuneTestOnEventReceive(sp& frontend, FrontendSettings settings); void tuneTestOnLock(sp& frontend, FrontendSettings settings); - void scanTestOnMessageLock(sp& frontend, FrontendSettings settings, - FrontendScanType type); + void scanTest(sp& frontend, FrontendConfig config, FrontendScanType type); + + // Helper methods + uint32_t getTargetFrequency(FrontendSettings settings, FrontendType type); + void resetBlindScanStartingFrequency(FrontendConfig config, uint32_t resetingFreq); private: bool mEventReceived = false; bool mScanMessageReceived = false; - bool mScanLockMessageReceived = false; - FrontendEventType mEventType; + bool mLockMsgReceived = false; + bool mScanMsgProcessed = true; FrontendScanMessageType mScanMessageType; FrontendScanMessage mScanMessage; hidl_vec mEventMessage; android::Mutex mMsgLock; android::Condition mMsgCondition; - uint8_t mOnEvenRetry = 0; + android::Condition mLockMsgCondition; }; void FrontendCallback::tuneTestOnEventReceive(sp& frontend, FrontendSettings settings) { Result result = frontend->tune(settings); - EXPECT_TRUE(result == Result::SUCCESS); android::Mutex::Autolock autoLock(mMsgLock); while (!mEventReceived) { if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) { - EXPECT_TRUE(false) << "event not received within timeout"; + EXPECT_TRUE(false) << "Event not received within timeout"; + mLockMsgReceived = false; return; } } @@ -233,61 +246,134 @@ void FrontendCallback::tuneTestOnEventReceive(sp& frontend, FrontendS void FrontendCallback::tuneTestOnLock(sp& frontend, FrontendSettings settings) { Result result = frontend->tune(settings); - EXPECT_TRUE(result == Result::SUCCESS); android::Mutex::Autolock autoLock(mMsgLock); -wait: - while (!mEventReceived) { - if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) { - EXPECT_TRUE(false) << "event not received within timeout"; + while (!mLockMsgReceived) { + if (-ETIMEDOUT == mLockMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) { + EXPECT_TRUE(false) << "Event LOCKED not received within timeout"; + mLockMsgReceived = false; return; } } - if (mEventType != FrontendEventType::LOCKED) { - ALOGD("[vts] frontend callback event received. Type: %d", mEventType); - mEventReceived = false; - if (mOnEvenRetry++ < FRONTEND_EVENT_CALLBACK_WAIT_COUNT) { - goto wait; - } - } - EXPECT_TRUE(mEventType == FrontendEventType::LOCKED) << "LOCK event not received"; - mEventReceived = false; - mOnEvenRetry = 0; + mLockMsgReceived = false; } -void FrontendCallback::scanTestOnMessageLock(sp& frontend, FrontendSettings settings, - FrontendScanType type) { - Result result = frontend->scan(settings, type); - EXPECT_TRUE(result == Result::SUCCESS); - android::Mutex::Autolock autoLock(mMsgLock); - int messagesCount = 0; +void FrontendCallback::scanTest(sp& frontend, FrontendConfig config, + FrontendScanType type) { + uint32_t targetFrequency = getTargetFrequency(config.settings, config.type); + if (type == FrontendScanType::SCAN_BLIND) { + // reset the frequency in the scan configuration to test blind scan. The settings param of + // passed in means the real input config on the transponder connected to the DUT. + // We want the blind the test to start from lower frequency than this to check the blind + // scan implementation. + resetBlindScanStartingFrequency(config, targetFrequency - 100); + } + Result result = frontend->scan(config.settings, type); + EXPECT_TRUE(result == Result::SUCCESS); + + bool scanMsgLockedReceived = false; + bool targetFrequencyReceived = false; + + android::Mutex::Autolock autoLock(mMsgLock); wait: - int count = 0; while (!mScanMessageReceived) { if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) { - ALOGD("[vts] waiting for scan message callback..."); - if (count++ > 10) { - EXPECT_TRUE(false) << "WAITING TOO LONG!!"; - return; - } + EXPECT_TRUE(false) << "Scan message not received within timeout"; + mScanMessageReceived = false; + mScanMsgProcessed = true; + return; } } if (mScanMessageType != FrontendScanMessageType::END) { - ALOGD("[vts] frontend scan message received. Type: %d", mScanMessageType); - mScanMessageReceived = false; - if (messagesCount++ > 3) { - EXPECT_TRUE(false) << "WAITING ON TOO MANY MSGS!!"; - return; + if (mScanMessageType == FrontendScanMessageType::LOCKED) { + scanMsgLockedReceived = true; + Result result = frontend->scan(config.settings, type); + EXPECT_TRUE(result == Result::SUCCESS); } + + if (mScanMessageType == FrontendScanMessageType::FREQUENCY) { + targetFrequencyReceived = mScanMessage.frequencies().size() > 0 && + mScanMessage.frequencies()[0] == targetFrequency; + } + + if (mScanMessageType == FrontendScanMessageType::PROGRESS_PERCENT) { + ALOGD("[vts] Scan in progress...[%d%%]", mScanMessage.progressPercent()); + } + + mScanMessageReceived = false; + mScanMsgProcessed = true; + mMsgCondition.signal(); goto wait; } - EXPECT_TRUE(mScanLockMessageReceived) << "scan lock message not received before scan ended"; + EXPECT_TRUE(scanMsgLockedReceived) << "Scan message LOCKED not received before END"; + EXPECT_TRUE(targetFrequencyReceived) << "frequency not received before LOCKED on blindScan"; mScanMessageReceived = false; - mScanLockMessageReceived = false; + mScanMsgProcessed = true; +} + +uint32_t FrontendCallback::getTargetFrequency(FrontendSettings settings, FrontendType type) { + switch (type) { + case FrontendType::ANALOG: + return settings.analog().frequency; + case FrontendType::ATSC: + return settings.atsc().frequency; + case FrontendType::ATSC3: + return settings.atsc3().frequency; + case FrontendType::DVBC: + return settings.dvbc().frequency; + case FrontendType::DVBS: + return settings.dvbs().frequency; + case FrontendType::DVBT: + return settings.dvbt().frequency; + case FrontendType::ISDBS: + return settings.isdbs().frequency; + case FrontendType::ISDBS3: + return settings.isdbs3().frequency; + case FrontendType::ISDBT: + return settings.isdbt().frequency; + default: + return 0; + } +} + +void FrontendCallback::resetBlindScanStartingFrequency(FrontendConfig config, + uint32_t resetingFreq) { + switch (config.type) { + case FrontendType::ANALOG: + config.settings.analog().frequency = resetingFreq; + break; + case FrontendType::ATSC: + config.settings.atsc().frequency = resetingFreq; + break; + case FrontendType::ATSC3: + config.settings.atsc3().frequency = resetingFreq; + break; + case FrontendType::DVBC: + config.settings.dvbc().frequency = resetingFreq; + break; + case FrontendType::DVBS: + config.settings.dvbs().frequency = resetingFreq; + break; + case FrontendType::DVBT: + config.settings.dvbt().frequency = resetingFreq; + break; + case FrontendType::ISDBS: + config.settings.isdbs().frequency = resetingFreq; + break; + case FrontendType::ISDBS3: + config.settings.isdbs3().frequency = resetingFreq; + break; + case FrontendType::ISDBT: + config.settings.isdbt().frequency = resetingFreq; + break; + default: + // do nothing + return; + } } /******************************** End FrontendCallback **********************************/ @@ -313,6 +399,7 @@ class FilterCallback : public IFilterCallback { } void setFilterId(uint32_t filterId) { mFilterId = filterId; } + void setFilterInterface(sp filter) { mFilter = filter; } void setFilterEventType(FilterEventType type) { mFilterEventType = type; } void testFilterDataOutput(); @@ -324,6 +411,7 @@ class FilterCallback : public IFilterCallback { void updateFilterMQ(MQDesc& filterMQDescriptor); void updateGoldenOutputMap(string goldenOutputFile); bool readFilterEventData(); + bool dumpAvData(DemuxFilterMediaEvent event); private: struct FilterThreadArgs { @@ -336,6 +424,7 @@ class FilterCallback : public IFilterCallback { string mFilterIdToGoldenOutput; uint32_t mFilterId; + sp mFilter; FilterEventType mFilterEventType; std::unique_ptr mFilterMQ; EventFlag* mFilterMQEventFlag; @@ -407,7 +496,7 @@ void FilterCallback::filterThreadLoop(DemuxFilterEvent& /* event */) { bool FilterCallback::readFilterEventData() { bool result = false; DemuxFilterEvent filterEvent = mFilterEvent; - ALOGW("[vts] reading from filter FMQ %d", mFilterId); + 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) { @@ -418,8 +507,7 @@ bool FilterCallback::readFilterEventData() { mDataLength = filterEvent.events[i].pes().dataLength; break; case FilterEventType::MEDIA: - mDataLength = filterEvent.events[i].media().dataLength; - break; + return dumpAvData(filterEvent.events[i].media()); case FilterEventType::RECORD: break; case FilterEventType::MMTPRECORD: @@ -443,6 +531,26 @@ bool FilterCallback::readFilterEventData() { mFilterMQEventFlag->wake(static_cast(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( + 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; +} /******************************** End FilterCallback **********************************/ /******************************** Start DvrCallback **********************************/ @@ -731,6 +839,7 @@ class TunerHidlTest : public testing::TestWithParam { sp mFilter; std::map> mFilters; std::map> mFilterCallbacks; + sp mFilterCallback; sp mDvrCallback; MQDesc mFilterMQDescriptor; @@ -826,7 +935,7 @@ AssertionResult TunerHidlTest::scanFrontend(FrontendConfig config, FrontendScanT EXPECT_TRUE(mFrontendInfo.type == config.type) << "FrontendConfig does not match the frontend info of the given id."; - mFrontendCallback->scanTestOnMessageLock(mFrontend, config.settings, type); + mFrontendCallback->scanTest(mFrontend, config, type); return AssertionResult(true); } @@ -926,13 +1035,14 @@ AssertionResult TunerHidlTest::getNewlyOpenedFilterId(uint32_t& filterId) { 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 || status == Result::UNAVAILABLE); + return AssertionResult(status == Result::SUCCESS); } AssertionResult TunerHidlTest::configFilter(DemuxFilterSettings setting, uint32_t filterId) { @@ -1300,7 +1410,6 @@ TEST_P(TunerHidlTest, TuneFrontend) { } ASSERT_TRUE(openFrontend(mFeIds[i])); ASSERT_TRUE(setFrontendCallback()); - ASSERT_TRUE(stopTuneFrontend()); ASSERT_TRUE(tuneFrontend(frontendArray[0])); ASSERT_TRUE(stopTuneFrontend()); ASSERT_TRUE(closeFrontend()); @@ -1320,13 +1429,31 @@ TEST_P(TunerHidlTest, AutoScanFrontend) { } ASSERT_TRUE(openFrontend(mFeIds[i])); ASSERT_TRUE(setFrontendCallback()); - ASSERT_TRUE(stopScanFrontend()); ASSERT_TRUE(scanFrontend(frontendScanArray[0], FrontendScanType::SCAN_AUTO)); ASSERT_TRUE(stopScanFrontend()); ASSERT_TRUE(closeFrontend()); break; } } + +TEST_P(TunerHidlTest, BlindScanFrontend) { + description("Run an blind frontend scan with specific setting and check lock scanMessage"); + 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 != frontendArray[0].type) { + continue; + } + ASSERT_TRUE(openFrontend(mFeIds[i])); + ASSERT_TRUE(setFrontendCallback()); + ASSERT_TRUE(scanFrontend(frontendScanArray[0], FrontendScanType::SCAN_BLIND)); + ASSERT_TRUE(stopScanFrontend()); + ASSERT_TRUE(closeFrontend()); + break; + } +} /*=============================== End Frontend Tests ===============================*/ /*============================ Start Demux/Filter Tests ============================*/ @@ -1535,6 +1662,39 @@ TEST_P(TunerHidlTest, RecordDataFlowWithTsRecordFilterTest) { ASSERT_TRUE(recordDataFlowTest(filterConf, recordSetting, goldenOutputFiles)); }*/ + +TEST_P(TunerHidlTest, AvBufferTest) { + description("Test the av filter data bufferring."); + + 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 != frontendArray[1].type) { + continue; + } + ASSERT_TRUE(openFrontend(mFeIds[i])); + ASSERT_TRUE(setFrontendCallback()); + ASSERT_TRUE(openDemux()); + ASSERT_TRUE(openFilterInDemux(filterArray[0].type)); + uint32_t filterId; + ASSERT_TRUE(getNewlyOpenedFilterId(filterId)); + ASSERT_TRUE(configFilter(filterArray[0].setting, filterId)); + ASSERT_TRUE(startFilter(filterId)); + ASSERT_TRUE(setDemuxFrontendDataSource(mFeIds[i])); + // tune test + ASSERT_TRUE(tuneFrontend(frontendArray[1])); + // broadcast data flow test + ASSERT_TRUE(broadcastDataFlowTest(goldenOutputFiles)); + ASSERT_TRUE(stopTuneFrontend()); + ASSERT_TRUE(stopFilter(filterId)); + ASSERT_TRUE(closeFilter(filterId)); + ASSERT_TRUE(closeDemux()); + ASSERT_TRUE(closeFrontend()); + break; + } +} /*============================== End Data Flow Tests ==============================*/ /******************************** End Test Entry **********************************/ } // namespace diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h index 55ca8579fb..31e3b51cd7 100644 --- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h +++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h @@ -64,7 +64,7 @@ using android::hardware::tv::tuner::V1_0::FrontendType; namespace { -#define frontend_transponders_count 1 +#define frontend_transponders_count 2 #define channels_count 1 #define frontend_scan_count 1 #define filter_count 2 @@ -108,13 +108,24 @@ inline void initFrontendConfig() { .standard = FrontendDvbtStandard::T, }; frontendArray[0].type = FrontendType::DVBT, frontendArray[0].settings.dvbt(dvbtSettings); + frontendArray[1].type = FrontendType::DVBS; }; /** Configuration array for the frontend scan test */ inline void initFrontendScanConfig() { - frontendScanArray[0].type = FrontendType::DVBT, frontendScanArray[0].settings.dvbt({ - .frequency = 577000, - }); + frontendScanArray[0].type = FrontendType::DVBT; + frontendScanArray[0].settings.dvbt({ + .frequency = 578000, + .transmissionMode = FrontendDvbtTransmissionMode::MODE_8K, + .bandwidth = FrontendDvbtBandwidth::BANDWIDTH_8MHZ, + .constellation = FrontendDvbtConstellation::AUTO, + .hierarchy = FrontendDvbtHierarchy::AUTO, + .hpCoderate = FrontendDvbtCoderate::AUTO, + .lpCoderate = FrontendDvbtCoderate::AUTO, + .guardInterval = FrontendDvbtGuardInterval::AUTO, + .isHighPriority = true, + .standard = FrontendDvbtStandard::T, + }); }; /** Configuration array for the filter test */ @@ -122,7 +133,7 @@ inline void initFilterConfig() { // TS Video filter setting filterArray[0].type.mainType = DemuxFilterMainType::TS; filterArray[0].type.subType.tsFilterType(DemuxTsFilterType::VIDEO); - filterArray[0].setting.ts().tpid = 49; + filterArray[0].setting.ts().tpid = 119; filterArray[0].setting.ts().filterSettings.av({.isPassthrough = false}); // TS PES filter setting filterArray[1].type.mainType = DemuxFilterMainType::TS; @@ -134,4 +145,4 @@ inline void initFilterConfig() { }); }; -} // namespace \ No newline at end of file +} // namespace