Loading tv/tuner/1.1/IFilter.hal +19 −0 Original line number Diff line number Diff line Loading @@ -51,4 +51,23 @@ interface IFilter extends @1.0::IFilter { * UNKNOWN_ERROR if failed for other reasons. */ configureIpCid(uint32_t ipCid) generates (Result result); /** * Get the shared AV memory handle. Use IFilter.releaseAvHandle to release the handle. * * When media filters are opened, call this API to initialize the share memory handle if it's * needed. * * If DemuxFilterMediaEvent.avMemory contains file descriptor, share memory should be ignored. * * @return avMemory A handle associated to the shared memory for audio or video. * avMemory.data[0] is normally an fd for ION memory. When the avMemory->numFd is 0, the * share memory is not initialized and does not contain valid fd. * avMemory.data[avMemory.numFds] is an index used as a parameter of * C2DataIdInfo to build C2 buffer in Codec. No C2 buffer would be created if the index * does not exist. * @return avMemSize the size of the shared av memory. It should be ignored when the share * memory is not initialized. */ getAvSharedHandle() generates (Result result, handle avMemory, uint64_t avMemSize); }; tv/tuner/1.1/default/Filter.cpp +167 −50 Original line number Diff line number Diff line Loading @@ -163,8 +163,17 @@ Return<Result> Filter::flush() { return Result::SUCCESS; } Return<Result> Filter::releaseAvHandle(const hidl_handle& /*avMemory*/, uint64_t avDataId) { Return<Result> Filter::releaseAvHandle(const hidl_handle& avMemory, uint64_t avDataId) { ALOGV("%s", __FUNCTION__); if ((avMemory.getNativeHandle()->numFds > 0) && (mSharedAvMemHandle.getNativeHandle()->numFds > 0) && (sameFile(avMemory.getNativeHandle()->data[0], mSharedAvMemHandle.getNativeHandle()->data[0]))) { freeSharedAvHandle(); return Result::SUCCESS; } if (mDataId2Avfd.find(avDataId) == mDataId2Avfd.end()) { return Result::INVALID_ARGUMENT; } Loading @@ -190,6 +199,35 @@ Return<Result> Filter::configureIpCid(uint32_t ipCid) { return Result::SUCCESS; } Return<void> Filter::getAvSharedHandle(getAvSharedHandle_cb _hidl_cb) { ALOGV("%s", __FUNCTION__); if (!mIsMediaFilter) { _hidl_cb(Result::INVALID_STATE, NULL, BUFFER_SIZE_16M); return Void(); } if (mSharedAvMemHandle.getNativeHandle() != nullptr) { _hidl_cb(Result::SUCCESS, mSharedAvMemHandle, BUFFER_SIZE_16M); return Void(); } int av_fd = createAvIonFd(BUFFER_SIZE_16M); if (av_fd == -1) { _hidl_cb(Result::UNKNOWN_ERROR, NULL, 0); } native_handle_t* nativeHandle = createNativeHandle(av_fd); if (nativeHandle == NULL) { _hidl_cb(Result::UNKNOWN_ERROR, NULL, 0); } mSharedAvMemHandle.setTo(nativeHandle, /*shouldOwn=*/true); ::close(av_fd); _hidl_cb(Result::SUCCESS, mSharedAvMemHandle, BUFFER_SIZE_16M); return Void(); } bool Filter::createFilterMQ() { ALOGV("%s", __FUNCTION__); Loading Loading @@ -313,8 +351,17 @@ void Filter::freeAvHandle() { } 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()); native_handle_delete(const_cast<native_handle_t*>( mFilterEvent.events[i].media().avMemory.getNativeHandle())); } } void Filter::freeSharedAvHandle() { if (!mIsMediaFilter) { return; } ::close(mSharedAvMemHandle.getNativeHandle()->data[0]); native_handle_delete(const_cast<native_handle_t*>(mSharedAvMemHandle.getNativeHandle())); } void Filter::maySendFilterStatusCallback() { Loading Loading @@ -509,8 +556,8 @@ Result Filter::startMediaFilterHandler() { return Result::SUCCESS; } if (mPts) { Result result; if (mPts) { result = createMediaFilterEventWithIon(mFilterOutput); if (result == Result::SUCCESS) { mFilterOutput.clear(); Loading Loading @@ -551,7 +598,10 @@ Result Filter::startMediaFilterHandler() { continue; } createMediaFilterEventWithIon(mPesOutput); result = createMediaFilterEventWithIon(mPesOutput); if (result != Result::SUCCESS) { return result; } } mFilterOutput.clear(); Loading @@ -560,51 +610,14 @@ Result Filter::startMediaFilterHandler() { } Result Filter::createMediaFilterEventWithIon(vector<uint8_t> output) { int av_fd = createAvIonFd(output.size()); if (av_fd == -1) { if (mUsingSharedAvMem) { if (mSharedAvMemHandle.getNativeHandle() == nullptr) { 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; return createShareMemMediaEvents(output); } 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 <dataId, av_fd> 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<uint32_t>(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; return createIndependentMediaEvents(output); } Result Filter::startRecordFilterHandler() { Loading Loading @@ -713,15 +726,119 @@ uint8_t* Filter::getIonBuffer(int fd, int size) { } native_handle_t* Filter::createNativeHandle(int fd) { native_handle_t* nativeHandle; if (fd < 0) { nativeHandle = native_handle_create(/*numFd*/ 0, 0); } else { // Create a native handle to pass the av fd via the callback event. native_handle_t* nativeHandle = native_handle_create(/*numFd*/ 1, 0); nativeHandle = native_handle_create(/*numFd*/ 1, 0); } if (nativeHandle == NULL) { ALOGE("[Filter] Failed to create native_handle %d", errno); return NULL; } if (nativeHandle->numFds > 0) { nativeHandle->data[0] = dup(fd); } return nativeHandle; } Result Filter::createIndependentMediaEvents(vector<uint8_t> 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 <dataId, av_fd> 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<uint32_t>(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::createShareMemMediaEvents(vector<uint8_t> output) { // copy the filtered data to the shared buffer uint8_t* sharedAvBuffer = getIonBuffer(mSharedAvMemHandle.getNativeHandle()->data[0], output.size() + mSharedAvMemOffset); if (sharedAvBuffer == NULL) { return Result::UNKNOWN_ERROR; } memcpy(sharedAvBuffer + mSharedAvMemOffset, output.data(), output.size() * sizeof(uint8_t)); // Create a memory handle with numFds == 0 native_handle_t* nativeHandle = createNativeHandle(-1); if (nativeHandle == NULL) { return Result::UNKNOWN_ERROR; } hidl_handle handle; handle.setTo(nativeHandle, /*shouldOwn=*/true); // Create mediaEvent and send callback DemuxFilterMediaEvent mediaEvent; mediaEvent = { .offset = static_cast<uint32_t>(mSharedAvMemOffset), .dataLength = static_cast<uint32_t>(output.size()), .avMemory = handle, }; mSharedAvMemOffset += output.size(); 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(); if (DEBUG_FILTER) { ALOGD("[Filter] shared av data length %d", mediaEvent.dataLength); } return Result::SUCCESS; } bool Filter::sameFile(int fd1, int fd2) { struct stat stat1, stat2; if (fstat(fd1, &stat1) < 0 || fstat(fd2, &stat2) < 0) { return false; } return (stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino); } } // namespace implementation } // namespace V1_0 } // namespace tuner Loading tv/tuner/1.1/default/Filter.h +13 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <inttypes.h> #include <ion/ion.h> #include <math.h> #include <sys/stat.h> #include <set> #include "Demux.h" #include "Dvr.h" Loading @@ -43,6 +44,7 @@ using ::android::hardware::MessageQueue; using ::android::hardware::MQDescriptorSync; using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>; const uint32_t BUFFER_SIZE_16M = 0x1000000; class Demux; class Dvr; Loading Loading @@ -78,6 +80,8 @@ class Filter : public V1_1::IFilter { virtual Return<Result> configureIpCid(uint32_t ipCid) override; virtual Return<void> getAvSharedHandle(getAvSharedHandle_cb _hidl_cb) override; /** * To create a FilterMQ and its Event Flag. * Loading @@ -93,6 +97,7 @@ class Filter : public V1_1::IFilter { void attachFilterToRecord(const sp<Dvr> dvr); void detachFilterFromRecord(); void freeAvHandle(); void freeSharedAvHandle(); bool isMediaFilter() { return mIsMediaFilter; }; bool isPcrFilter() { return mIsPcrFilter; }; bool isRecordFilter() { return mIsRecordFilter; }; Loading Loading @@ -185,6 +190,9 @@ class Filter : public V1_1::IFilter { uint8_t* getIonBuffer(int fd, int size); native_handle_t* createNativeHandle(int fd); Result createMediaFilterEventWithIon(vector<uint8_t> output); Result createIndependentMediaEvents(vector<uint8_t> output); Result createShareMemMediaEvents(vector<uint8_t> output); bool sameFile(int fd1, int fd2); /** * Lock to protect writes to the FMQs Loading Loading @@ -212,6 +220,11 @@ class Filter : public V1_1::IFilter { std::map<uint64_t, int> mDataId2Avfd; uint64_t mLastUsedDataId = 1; int mAvBufferCopyCount = 0; // Shared A/V memory handle hidl_handle mSharedAvMemHandle; bool mUsingSharedAvMem = true; uint32_t mSharedAvMemOffset = 0; }; } // namespace implementation Loading tv/tuner/1.1/vts/functional/FilterTests.cpp +80 −5 Original line number Diff line number Diff line Loading @@ -16,22 +16,70 @@ #include "FilterTests.h" bool FilterCallback::readFilterEventData() { bool result = false; ALOGW("[vts] reading from filter FMQ or buffer %d", mFilterId); 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::readFilterEventData() { ALOGW("[vts] reading filter event"); // todo separate filter handlers for (int i = 0; i < mFilterEvent.events.size(); i++) { auto event = mFilterEvent.events[i]; switch (event.getDiscriminator()) { case DemuxFilterEvent::Event::hidl_discriminator::media: ALOGD("[vts] Media filter event, avMemHandle numFds=%d.", event.media().avMemory.getNativeHandle()->numFds); dumpAvData(event.media()); break; default: break; } } for (int i = 0; i < mFilterEventExt.events.size(); i++) { auto eventExt = mFilterEventExt.events[i]; switch (eventExt.getDiscriminator()) { case DemuxFilterEventExt::Event::hidl_discriminator::tsRecord: ALOGW("[vts] Extended TS record filter event, pts=%" PRIu64 ".", ALOGD("[vts] Extended TS record filter event, pts=%" PRIu64 ".", eventExt.tsRecord().pts); break; default: break; } } return result; } bool FilterCallback::dumpAvData(DemuxFilterMediaEvent event) { uint32_t length = event.dataLength; uint32_t offset = event.offset; // read data from buffer pointed by a handle hidl_handle handle = event.avMemory; if (handle.getNativeHandle()->numFds == 0) { if (mAvSharedHandle == NULL) { return false; } handle = mAvSharedHandle; } 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)); if (buffer == MAP_FAILED) { ALOGE("[vts] fail to allocate av buffer, errno=%d", errno); return false; } uint8_t output[length + 1]; memcpy(output, buffer + offset, length); // print buffer and check with golden output. ::close(av_fd); return true; } AssertionResult FilterTests::openFilterInDemux(DemuxFilterType type, uint32_t bufferSize) { Loading Loading @@ -81,6 +129,33 @@ AssertionResult FilterTests::getNewlyOpenedFilterId_64bit(uint64_t& filterId) { return AssertionResult(status == Result::SUCCESS); } AssertionResult FilterTests::getSharedAvMemoryHandle(uint64_t filterId) { EXPECT_TRUE(mFilters[filterId]) << "Open media filter first."; Result status = Result::UNKNOWN_ERROR; sp<android::hardware::tv::tuner::V1_1::IFilter> filter_v1_1 = android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilters[filterId]); if (filter_v1_1 != NULL) { filter_v1_1->getAvSharedHandle([&](Result r, hidl_handle avMemory, uint64_t avMemSize) { status = r; if (status == Result::SUCCESS) { mFilterCallbacks[mFilterId]->setSharedHandle(avMemory); mFilterCallbacks[mFilterId]->setMemSize(avMemSize); mAvSharedHandle = avMemory; } }); } return AssertionResult(status == Result::SUCCESS); } AssertionResult FilterTests::releaseShareAvHandle(uint64_t filterId) { Result status; EXPECT_TRUE(mFilters[filterId]) << "Open media filter first."; EXPECT_TRUE(mAvSharedHandle) << "No shared av handle to release."; status = mFilters[filterId]->releaseAvHandle(mAvSharedHandle, 0 /*dataId*/); return AssertionResult(status == Result::SUCCESS); } AssertionResult FilterTests::configFilter(DemuxFilterSettings setting, uint64_t filterId) { Result status; EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first."; Loading tv/tuner/1.1/vts/functional/FilterTests.h +25 −5 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ 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::DemuxFilterSettings; using android::hardware::tv::tuner::V1_0::DemuxFilterStatus; using android::hardware::tv::tuner::V1_0::DemuxFilterType; Loading Loading @@ -93,7 +94,14 @@ class FilterCallback : public IFilterCallback { } virtual Return<void> onFilterEvent( const android::hardware::tv::tuner::V1_0::DemuxFilterEvent& /*filterEvent*/) override { const android::hardware::tv::tuner::V1_0::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++; mMsgCondition.signal(); return Void(); } Loading @@ -104,8 +112,13 @@ class FilterCallback : public IFilterCallback { void setFilterId(uint32_t filterId) { mFilterId = filterId; } void setFilterInterface(sp<IFilter> filter) { mFilter = filter; } void setFilterEventType(FilterEventType type) { mFilterEventType = type; } void setSharedHandle(hidl_handle sharedHandle) { mAvSharedHandle = sharedHandle; } void setMemSize(uint64_t size) { mAvSharedMemSize = size; } void testFilterDataOutput(); bool readFilterEventData(); void readFilterEventData(); bool dumpAvData(DemuxFilterMediaEvent event); private: uint32_t mFilterId; Loading @@ -114,6 +127,9 @@ class FilterCallback : public IFilterCallback { DemuxFilterEvent mFilterEvent; DemuxFilterEventExt mFilterEventExt; hidl_handle mAvSharedHandle = NULL; uint64_t mAvSharedMemSize = -1; android::Mutex mMsgLock; android::Mutex mFilterOutputLock; android::Condition mMsgCondition; Loading @@ -127,10 +143,12 @@ class FilterTests { void setDemux(sp<IDemux> demux) { mDemux = demux; } sp<IFilter> getFilterById(uint64_t filterId) { return mFilters[filterId]; } std::map<uint64_t, sp<FilterCallback>> getFilterCallbacks() { return mFilterCallbacks; } map<uint64_t, sp<FilterCallback>> getFilterCallbacks() { return mFilterCallbacks; } AssertionResult openFilterInDemux(DemuxFilterType type, uint32_t bufferSize); AssertionResult getNewlyOpenedFilterId_64bit(uint64_t& filterId); AssertionResult getSharedAvMemoryHandle(uint64_t filterId); AssertionResult releaseShareAvHandle(uint64_t filterId); AssertionResult configFilter(DemuxFilterSettings setting, uint64_t filterId); AssertionResult configIpFilterCid(uint32_t ipCid, uint64_t filterId); AssertionResult getFilterMQDescriptor(uint64_t filterId); Loading Loading @@ -193,12 +211,14 @@ class FilterTests { sp<ITuner> mService; sp<IFilter> mFilter; sp<IDemux> mDemux; std::map<uint64_t, sp<IFilter>> mFilters; std::map<uint64_t, sp<FilterCallback>> mFilterCallbacks; map<uint64_t, sp<IFilter>> mFilters; map<uint64_t, sp<FilterCallback>> mFilterCallbacks; sp<FilterCallback> mFilterCallback; MQDesc mFilterMQDescriptor; vector<uint64_t> mUsedFilterIds; hidl_handle mAvSharedHandle = NULL; uint64_t mFilterId = -1; }; Loading
tv/tuner/1.1/IFilter.hal +19 −0 Original line number Diff line number Diff line Loading @@ -51,4 +51,23 @@ interface IFilter extends @1.0::IFilter { * UNKNOWN_ERROR if failed for other reasons. */ configureIpCid(uint32_t ipCid) generates (Result result); /** * Get the shared AV memory handle. Use IFilter.releaseAvHandle to release the handle. * * When media filters are opened, call this API to initialize the share memory handle if it's * needed. * * If DemuxFilterMediaEvent.avMemory contains file descriptor, share memory should be ignored. * * @return avMemory A handle associated to the shared memory for audio or video. * avMemory.data[0] is normally an fd for ION memory. When the avMemory->numFd is 0, the * share memory is not initialized and does not contain valid fd. * avMemory.data[avMemory.numFds] is an index used as a parameter of * C2DataIdInfo to build C2 buffer in Codec. No C2 buffer would be created if the index * does not exist. * @return avMemSize the size of the shared av memory. It should be ignored when the share * memory is not initialized. */ getAvSharedHandle() generates (Result result, handle avMemory, uint64_t avMemSize); };
tv/tuner/1.1/default/Filter.cpp +167 −50 Original line number Diff line number Diff line Loading @@ -163,8 +163,17 @@ Return<Result> Filter::flush() { return Result::SUCCESS; } Return<Result> Filter::releaseAvHandle(const hidl_handle& /*avMemory*/, uint64_t avDataId) { Return<Result> Filter::releaseAvHandle(const hidl_handle& avMemory, uint64_t avDataId) { ALOGV("%s", __FUNCTION__); if ((avMemory.getNativeHandle()->numFds > 0) && (mSharedAvMemHandle.getNativeHandle()->numFds > 0) && (sameFile(avMemory.getNativeHandle()->data[0], mSharedAvMemHandle.getNativeHandle()->data[0]))) { freeSharedAvHandle(); return Result::SUCCESS; } if (mDataId2Avfd.find(avDataId) == mDataId2Avfd.end()) { return Result::INVALID_ARGUMENT; } Loading @@ -190,6 +199,35 @@ Return<Result> Filter::configureIpCid(uint32_t ipCid) { return Result::SUCCESS; } Return<void> Filter::getAvSharedHandle(getAvSharedHandle_cb _hidl_cb) { ALOGV("%s", __FUNCTION__); if (!mIsMediaFilter) { _hidl_cb(Result::INVALID_STATE, NULL, BUFFER_SIZE_16M); return Void(); } if (mSharedAvMemHandle.getNativeHandle() != nullptr) { _hidl_cb(Result::SUCCESS, mSharedAvMemHandle, BUFFER_SIZE_16M); return Void(); } int av_fd = createAvIonFd(BUFFER_SIZE_16M); if (av_fd == -1) { _hidl_cb(Result::UNKNOWN_ERROR, NULL, 0); } native_handle_t* nativeHandle = createNativeHandle(av_fd); if (nativeHandle == NULL) { _hidl_cb(Result::UNKNOWN_ERROR, NULL, 0); } mSharedAvMemHandle.setTo(nativeHandle, /*shouldOwn=*/true); ::close(av_fd); _hidl_cb(Result::SUCCESS, mSharedAvMemHandle, BUFFER_SIZE_16M); return Void(); } bool Filter::createFilterMQ() { ALOGV("%s", __FUNCTION__); Loading Loading @@ -313,8 +351,17 @@ void Filter::freeAvHandle() { } 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()); native_handle_delete(const_cast<native_handle_t*>( mFilterEvent.events[i].media().avMemory.getNativeHandle())); } } void Filter::freeSharedAvHandle() { if (!mIsMediaFilter) { return; } ::close(mSharedAvMemHandle.getNativeHandle()->data[0]); native_handle_delete(const_cast<native_handle_t*>(mSharedAvMemHandle.getNativeHandle())); } void Filter::maySendFilterStatusCallback() { Loading Loading @@ -509,8 +556,8 @@ Result Filter::startMediaFilterHandler() { return Result::SUCCESS; } if (mPts) { Result result; if (mPts) { result = createMediaFilterEventWithIon(mFilterOutput); if (result == Result::SUCCESS) { mFilterOutput.clear(); Loading Loading @@ -551,7 +598,10 @@ Result Filter::startMediaFilterHandler() { continue; } createMediaFilterEventWithIon(mPesOutput); result = createMediaFilterEventWithIon(mPesOutput); if (result != Result::SUCCESS) { return result; } } mFilterOutput.clear(); Loading @@ -560,51 +610,14 @@ Result Filter::startMediaFilterHandler() { } Result Filter::createMediaFilterEventWithIon(vector<uint8_t> output) { int av_fd = createAvIonFd(output.size()); if (av_fd == -1) { if (mUsingSharedAvMem) { if (mSharedAvMemHandle.getNativeHandle() == nullptr) { 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; return createShareMemMediaEvents(output); } 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 <dataId, av_fd> 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<uint32_t>(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; return createIndependentMediaEvents(output); } Result Filter::startRecordFilterHandler() { Loading Loading @@ -713,15 +726,119 @@ uint8_t* Filter::getIonBuffer(int fd, int size) { } native_handle_t* Filter::createNativeHandle(int fd) { native_handle_t* nativeHandle; if (fd < 0) { nativeHandle = native_handle_create(/*numFd*/ 0, 0); } else { // Create a native handle to pass the av fd via the callback event. native_handle_t* nativeHandle = native_handle_create(/*numFd*/ 1, 0); nativeHandle = native_handle_create(/*numFd*/ 1, 0); } if (nativeHandle == NULL) { ALOGE("[Filter] Failed to create native_handle %d", errno); return NULL; } if (nativeHandle->numFds > 0) { nativeHandle->data[0] = dup(fd); } return nativeHandle; } Result Filter::createIndependentMediaEvents(vector<uint8_t> 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 <dataId, av_fd> 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<uint32_t>(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::createShareMemMediaEvents(vector<uint8_t> output) { // copy the filtered data to the shared buffer uint8_t* sharedAvBuffer = getIonBuffer(mSharedAvMemHandle.getNativeHandle()->data[0], output.size() + mSharedAvMemOffset); if (sharedAvBuffer == NULL) { return Result::UNKNOWN_ERROR; } memcpy(sharedAvBuffer + mSharedAvMemOffset, output.data(), output.size() * sizeof(uint8_t)); // Create a memory handle with numFds == 0 native_handle_t* nativeHandle = createNativeHandle(-1); if (nativeHandle == NULL) { return Result::UNKNOWN_ERROR; } hidl_handle handle; handle.setTo(nativeHandle, /*shouldOwn=*/true); // Create mediaEvent and send callback DemuxFilterMediaEvent mediaEvent; mediaEvent = { .offset = static_cast<uint32_t>(mSharedAvMemOffset), .dataLength = static_cast<uint32_t>(output.size()), .avMemory = handle, }; mSharedAvMemOffset += output.size(); 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(); if (DEBUG_FILTER) { ALOGD("[Filter] shared av data length %d", mediaEvent.dataLength); } return Result::SUCCESS; } bool Filter::sameFile(int fd1, int fd2) { struct stat stat1, stat2; if (fstat(fd1, &stat1) < 0 || fstat(fd2, &stat2) < 0) { return false; } return (stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino); } } // namespace implementation } // namespace V1_0 } // namespace tuner Loading
tv/tuner/1.1/default/Filter.h +13 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <inttypes.h> #include <ion/ion.h> #include <math.h> #include <sys/stat.h> #include <set> #include "Demux.h" #include "Dvr.h" Loading @@ -43,6 +44,7 @@ using ::android::hardware::MessageQueue; using ::android::hardware::MQDescriptorSync; using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>; const uint32_t BUFFER_SIZE_16M = 0x1000000; class Demux; class Dvr; Loading Loading @@ -78,6 +80,8 @@ class Filter : public V1_1::IFilter { virtual Return<Result> configureIpCid(uint32_t ipCid) override; virtual Return<void> getAvSharedHandle(getAvSharedHandle_cb _hidl_cb) override; /** * To create a FilterMQ and its Event Flag. * Loading @@ -93,6 +97,7 @@ class Filter : public V1_1::IFilter { void attachFilterToRecord(const sp<Dvr> dvr); void detachFilterFromRecord(); void freeAvHandle(); void freeSharedAvHandle(); bool isMediaFilter() { return mIsMediaFilter; }; bool isPcrFilter() { return mIsPcrFilter; }; bool isRecordFilter() { return mIsRecordFilter; }; Loading Loading @@ -185,6 +190,9 @@ class Filter : public V1_1::IFilter { uint8_t* getIonBuffer(int fd, int size); native_handle_t* createNativeHandle(int fd); Result createMediaFilterEventWithIon(vector<uint8_t> output); Result createIndependentMediaEvents(vector<uint8_t> output); Result createShareMemMediaEvents(vector<uint8_t> output); bool sameFile(int fd1, int fd2); /** * Lock to protect writes to the FMQs Loading Loading @@ -212,6 +220,11 @@ class Filter : public V1_1::IFilter { std::map<uint64_t, int> mDataId2Avfd; uint64_t mLastUsedDataId = 1; int mAvBufferCopyCount = 0; // Shared A/V memory handle hidl_handle mSharedAvMemHandle; bool mUsingSharedAvMem = true; uint32_t mSharedAvMemOffset = 0; }; } // namespace implementation Loading
tv/tuner/1.1/vts/functional/FilterTests.cpp +80 −5 Original line number Diff line number Diff line Loading @@ -16,22 +16,70 @@ #include "FilterTests.h" bool FilterCallback::readFilterEventData() { bool result = false; ALOGW("[vts] reading from filter FMQ or buffer %d", mFilterId); 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::readFilterEventData() { ALOGW("[vts] reading filter event"); // todo separate filter handlers for (int i = 0; i < mFilterEvent.events.size(); i++) { auto event = mFilterEvent.events[i]; switch (event.getDiscriminator()) { case DemuxFilterEvent::Event::hidl_discriminator::media: ALOGD("[vts] Media filter event, avMemHandle numFds=%d.", event.media().avMemory.getNativeHandle()->numFds); dumpAvData(event.media()); break; default: break; } } for (int i = 0; i < mFilterEventExt.events.size(); i++) { auto eventExt = mFilterEventExt.events[i]; switch (eventExt.getDiscriminator()) { case DemuxFilterEventExt::Event::hidl_discriminator::tsRecord: ALOGW("[vts] Extended TS record filter event, pts=%" PRIu64 ".", ALOGD("[vts] Extended TS record filter event, pts=%" PRIu64 ".", eventExt.tsRecord().pts); break; default: break; } } return result; } bool FilterCallback::dumpAvData(DemuxFilterMediaEvent event) { uint32_t length = event.dataLength; uint32_t offset = event.offset; // read data from buffer pointed by a handle hidl_handle handle = event.avMemory; if (handle.getNativeHandle()->numFds == 0) { if (mAvSharedHandle == NULL) { return false; } handle = mAvSharedHandle; } 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)); if (buffer == MAP_FAILED) { ALOGE("[vts] fail to allocate av buffer, errno=%d", errno); return false; } uint8_t output[length + 1]; memcpy(output, buffer + offset, length); // print buffer and check with golden output. ::close(av_fd); return true; } AssertionResult FilterTests::openFilterInDemux(DemuxFilterType type, uint32_t bufferSize) { Loading Loading @@ -81,6 +129,33 @@ AssertionResult FilterTests::getNewlyOpenedFilterId_64bit(uint64_t& filterId) { return AssertionResult(status == Result::SUCCESS); } AssertionResult FilterTests::getSharedAvMemoryHandle(uint64_t filterId) { EXPECT_TRUE(mFilters[filterId]) << "Open media filter first."; Result status = Result::UNKNOWN_ERROR; sp<android::hardware::tv::tuner::V1_1::IFilter> filter_v1_1 = android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilters[filterId]); if (filter_v1_1 != NULL) { filter_v1_1->getAvSharedHandle([&](Result r, hidl_handle avMemory, uint64_t avMemSize) { status = r; if (status == Result::SUCCESS) { mFilterCallbacks[mFilterId]->setSharedHandle(avMemory); mFilterCallbacks[mFilterId]->setMemSize(avMemSize); mAvSharedHandle = avMemory; } }); } return AssertionResult(status == Result::SUCCESS); } AssertionResult FilterTests::releaseShareAvHandle(uint64_t filterId) { Result status; EXPECT_TRUE(mFilters[filterId]) << "Open media filter first."; EXPECT_TRUE(mAvSharedHandle) << "No shared av handle to release."; status = mFilters[filterId]->releaseAvHandle(mAvSharedHandle, 0 /*dataId*/); return AssertionResult(status == Result::SUCCESS); } AssertionResult FilterTests::configFilter(DemuxFilterSettings setting, uint64_t filterId) { Result status; EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first."; Loading
tv/tuner/1.1/vts/functional/FilterTests.h +25 −5 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ 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::DemuxFilterSettings; using android::hardware::tv::tuner::V1_0::DemuxFilterStatus; using android::hardware::tv::tuner::V1_0::DemuxFilterType; Loading Loading @@ -93,7 +94,14 @@ class FilterCallback : public IFilterCallback { } virtual Return<void> onFilterEvent( const android::hardware::tv::tuner::V1_0::DemuxFilterEvent& /*filterEvent*/) override { const android::hardware::tv::tuner::V1_0::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++; mMsgCondition.signal(); return Void(); } Loading @@ -104,8 +112,13 @@ class FilterCallback : public IFilterCallback { void setFilterId(uint32_t filterId) { mFilterId = filterId; } void setFilterInterface(sp<IFilter> filter) { mFilter = filter; } void setFilterEventType(FilterEventType type) { mFilterEventType = type; } void setSharedHandle(hidl_handle sharedHandle) { mAvSharedHandle = sharedHandle; } void setMemSize(uint64_t size) { mAvSharedMemSize = size; } void testFilterDataOutput(); bool readFilterEventData(); void readFilterEventData(); bool dumpAvData(DemuxFilterMediaEvent event); private: uint32_t mFilterId; Loading @@ -114,6 +127,9 @@ class FilterCallback : public IFilterCallback { DemuxFilterEvent mFilterEvent; DemuxFilterEventExt mFilterEventExt; hidl_handle mAvSharedHandle = NULL; uint64_t mAvSharedMemSize = -1; android::Mutex mMsgLock; android::Mutex mFilterOutputLock; android::Condition mMsgCondition; Loading @@ -127,10 +143,12 @@ class FilterTests { void setDemux(sp<IDemux> demux) { mDemux = demux; } sp<IFilter> getFilterById(uint64_t filterId) { return mFilters[filterId]; } std::map<uint64_t, sp<FilterCallback>> getFilterCallbacks() { return mFilterCallbacks; } map<uint64_t, sp<FilterCallback>> getFilterCallbacks() { return mFilterCallbacks; } AssertionResult openFilterInDemux(DemuxFilterType type, uint32_t bufferSize); AssertionResult getNewlyOpenedFilterId_64bit(uint64_t& filterId); AssertionResult getSharedAvMemoryHandle(uint64_t filterId); AssertionResult releaseShareAvHandle(uint64_t filterId); AssertionResult configFilter(DemuxFilterSettings setting, uint64_t filterId); AssertionResult configIpFilterCid(uint32_t ipCid, uint64_t filterId); AssertionResult getFilterMQDescriptor(uint64_t filterId); Loading Loading @@ -193,12 +211,14 @@ class FilterTests { sp<ITuner> mService; sp<IFilter> mFilter; sp<IDemux> mDemux; std::map<uint64_t, sp<IFilter>> mFilters; std::map<uint64_t, sp<FilterCallback>> mFilterCallbacks; map<uint64_t, sp<IFilter>> mFilters; map<uint64_t, sp<FilterCallback>> mFilterCallbacks; sp<FilterCallback> mFilterCallback; MQDesc mFilterMQDescriptor; vector<uint64_t> mUsedFilterIds; hidl_handle mAvSharedHandle = NULL; uint64_t mFilterId = -1; };