Copy filtered av data to ion buffer to test on cuttlefish
Test: atest VtsHalTvTunerV1_0TargetTest
Bug: 150952766
Change-Id: If007f9c021102dc95be8e9dc70be70d3945192a9
diff --git a/tv/tuner/1.0/default/Android.bp b/tv/tuner/1.0/default/Android.bp
index 989e25c..5711889 100644
--- a/tv/tuner/1.0/default/Android.bp
+++ b/tv/tuner/1.0/default/Android.bp
@@ -24,6 +24,7 @@
"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 54d0952..f610c60 100644
--- a/tv/tuner/1.0/default/Filter.cpp
+++ b/tv/tuner/1.0/default/Filter.cpp
@@ -60,6 +60,8 @@
Return<void> Filter::getQueueDesc(getQueueDesc_cb _hidl_cb) {
ALOGV("%s", __FUNCTION__);
+ mIsUsingFMQ = true;
+
_hidl_cb(Result::SUCCESS, *mFilterMQ->getDesc());
return Void();
}
@@ -120,9 +122,13 @@
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 (mDataId2Avfd.find(avDataId) == mDataId2Avfd.end()) {
+ return Result::INVALID_ARGUMENT;
+ }
+ ::close(mDataId2Avfd[avDataId]);
return Result::SUCCESS;
}
@@ -174,14 +180,21 @@
// 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 @@
// 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<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED), &efState,
WAIT_TIMEOUT, true /* retry on spurious wake */);
@@ -202,11 +215,6 @@
break;
}
- if (mCallback == nullptr) {
- ALOGD("[Filter] filter %d does not hava callback. Ending thread", mFilterId);
- break;
- }
-
maySendFilterStatusCallback();
while (mFilterThreadRunning) {
@@ -232,7 +240,22 @@
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<std::mutex> lock(mFilterStatusLock);
int availableToRead = mFilterMQ->availableToRead();
int availableToWrite = mFilterMQ->availableToWrite();
@@ -409,19 +432,88 @@
}
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<std::mutex> 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<uint8_t>::const_iterator first = mFilterOutput.begin() + i + 4;
+ vector<uint8_t>::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 <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>(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 @@
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<uint8_t*>(
+ 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 0dc992a..afed98e 100644
--- a/tv/tuner/1.0/default/Filter.h
+++ b/tv/tuner/1.0/default/Filter.h
@@ -19,6 +19,7 @@
#include <android/hardware/tv/tuner/1.0/IFilter.h>
#include <fmq/MessageQueue.h>
+#include <ion/ion.h>
#include <math.h>
#include <set>
#include "Demux.h"
@@ -87,6 +88,7 @@
Result startRecordFilterHandler();
void attachFilterToRecord(const sp<Dvr> dvr);
void detachFilterFromRecord();
+ void freeAvHandle();
private:
// Tuner service
@@ -109,6 +111,7 @@
vector<uint8_t> mFilterOutput;
vector<uint8_t> mRecordFilterOutput;
unique_ptr<FilterMQ> mFilterMQ;
+ bool mIsUsingFMQ = false;
EventFlag* mFilterEventFlag;
DemuxFilterEvent mFilterEvent;
@@ -160,6 +163,10 @@
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 @@
// TODO handle mulptiple Pes filters
int mPesSizeLeft = 0;
vector<uint8_t> mPesOutput;
+
+ // A map from data id to ion handle
+ std::map<uint64_t, int> 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 7e206a7..bb0d8dc 100644
--- a/tv/tuner/1.0/default/Frontend.cpp
+++ b/tv/tuner/1.0/default/Frontend.cpp
@@ -63,9 +63,6 @@
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 @@
}
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 eab43a3..b954639 100644
--- a/tv/tuner/1.0/default/Frontend.h
+++ b/tv/tuner/1.0/default/Frontend.h
@@ -76,7 +76,6 @@
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 4fd3355..8fb5061 100644
--- a/tv/tuner/1.0/default/Tuner.cpp
+++ b/tv/tuner/1.0/default/Tuner.cpp
@@ -106,7 +106,7 @@
return Void();
}
-Return<void> Tuner::getFrontendInfo(FrontendId /* frontendId */, getFrontendInfo_cb _hidl_cb) {
+Return<void> Tuner::getFrontendInfo(FrontendId /*frontendId*/, getFrontendInfo_cb _hidl_cb) {
ALOGV("%s", __FUNCTION__);
vector<FrontendStatusType> statusCaps = {
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index f693e7c..bfc077f 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -57,6 +57,7 @@
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 +69,7 @@
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;
@@ -313,6 +315,7 @@
}
void setFilterId(uint32_t filterId) { mFilterId = filterId; }
+ void setFilterInterface(sp<IFilter> filter) { mFilter = filter; }
void setFilterEventType(FilterEventType type) { mFilterEventType = type; }
void testFilterDataOutput();
@@ -324,6 +327,7 @@
void updateFilterMQ(MQDesc& filterMQDescriptor);
void updateGoldenOutputMap(string goldenOutputFile);
bool readFilterEventData();
+ bool dumpAvData(DemuxFilterMediaEvent event);
private:
struct FilterThreadArgs {
@@ -336,6 +340,7 @@
string mFilterIdToGoldenOutput;
uint32_t mFilterId;
+ sp<IFilter> mFilter;
FilterEventType mFilterEventType;
std::unique_ptr<FilterMQ> mFilterMQ;
EventFlag* mFilterMQEventFlag;
@@ -407,7 +412,7 @@
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 +423,7 @@
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 +447,26 @@
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;
+}
/******************************** End FilterCallback **********************************/
/******************************** Start DvrCallback **********************************/
@@ -731,6 +755,7 @@
sp<IFilter> mFilter;
std::map<uint32_t, sp<IFilter>> mFilters;
std::map<uint32_t, sp<FilterCallback>> mFilterCallbacks;
+
sp<FilterCallback> mFilterCallback;
sp<DvrCallback> mDvrCallback;
MQDesc mFilterMQDescriptor;
@@ -926,6 +951,7 @@
if (status == Result::SUCCESS) {
mFilterCallback->setFilterId(mFilterId);
+ mFilterCallback->setFilterInterface(mFilter);
mUsedFilterIds.insert(mUsedFilterIds.end(), mFilterId);
mFilters[mFilterId] = mFilter;
mFilterCallbacks[mFilterId] = mFilterCallback;
@@ -1535,6 +1561,39 @@
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 55ca857..25612d7 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 @@
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,6 +108,7 @@
.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 */
@@ -122,7 +123,7 @@
// 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;