Merge "Add IWifiApIface@1.4 (configurable MAC address)"
diff --git a/sensors/1.0/default/android.hardware.sensors@1.0-service.rc b/sensors/1.0/default/android.hardware.sensors@1.0-service.rc
index 4faa562..b41730b 100644
--- a/sensors/1.0/default/android.hardware.sensors@1.0-service.rc
+++ b/sensors/1.0/default/android.hardware.sensors@1.0-service.rc
@@ -2,6 +2,6 @@
interface android.hardware.sensors@1.0::ISensors default
class hal
user system
- group system wakelock
+ group system wakelock uhid
capabilities BLOCK_SUSPEND
rlimit rtprio 10 10
diff --git a/tv/tuner/1.0/IDemux.hal b/tv/tuner/1.0/IDemux.hal
index e03095b..7fd7e26 100644
--- a/tv/tuner/1.0/IDemux.hal
+++ b/tv/tuner/1.0/IDemux.hal
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package android.hardware.tv.tuner@1.0;
import IDemuxCallback;
@@ -46,9 +62,9 @@
*
* It is used by the client to get the descriptor of the filter's Fast
* Message Queue. The data in FMQ is filtered out from MPEG transport
- * stream. The data is origanized to data blocks which may have
+ * stream. The data is organized to data blocks which may have
* different length. The length's information of one or multiple data blocks
- * is sent to client throught DemuxFilterEvent.
+ * is sent to client through DemuxFilterEvent.
*
* @param filterId the ID of the filter.
* @return result Result status of the operation.
@@ -81,7 +97,7 @@
/**
* Start the filter.
*
- * It is used by the client to ask the filter to start filterring data.
+ * It is used by the client to ask the filter to start filtering data.
*
* @param filterId the ID of the filter.
* @return result Result status of the operation.
@@ -202,7 +218,7 @@
*
* It is used by the client to get the descriptor of the output's Fast
* Message Queue. The data in FMQ is muxed packets output from selected
- * filters. The packet's format is specifed by DemuxDataFormat in
+ * filters. The packet's format is specified by DemuxDataFormat in
* DemuxOutputSettings.
*
* @return result Result status of the operation.
@@ -236,7 +252,7 @@
* INVALID_STATE if failed for wrong state.
* UNKNOWN_ERROR if failed for other reasons.
*/
- attachOutputTsFilter(DemuxFilterId filterId) generates (Result result);
+ attachOutputFilter(DemuxFilterId filterId) generates (Result result);
/**
* Detach one filter from the demux's output.
@@ -250,7 +266,7 @@
* INVALID_STATE if failed for wrong state.
* UNKNOWN_ERROR if failed for other reasons.
*/
- detachOutputTsFilter(DemuxFilterId filterId) generates (Result result);
+ detachOutputFilter(DemuxFilterId filterId) generates (Result result);
/**
* Start to take data to the demux's output.
diff --git a/tv/tuner/1.0/IDemuxCallback.hal b/tv/tuner/1.0/IDemuxCallback.hal
index 55e8420..7bce9ef 100644
--- a/tv/tuner/1.0/IDemuxCallback.hal
+++ b/tv/tuner/1.0/IDemuxCallback.hal
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package android.hardware.tv.tuner@1.0;
interface IDemuxCallback {
diff --git a/tv/tuner/1.0/IDescrambler.hal b/tv/tuner/1.0/IDescrambler.hal
index d078657..61ff1df 100644
--- a/tv/tuner/1.0/IDescrambler.hal
+++ b/tv/tuner/1.0/IDescrambler.hal
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package android.hardware.tv.tuner@1.0;
/**
* Descrambler is used to descramble input data.
diff --git a/tv/tuner/1.0/IFrontend.hal b/tv/tuner/1.0/IFrontend.hal
index 8788643..83e390d 100644
--- a/tv/tuner/1.0/IFrontend.hal
+++ b/tv/tuner/1.0/IFrontend.hal
@@ -145,10 +145,10 @@
* cable frontend.
* UNKNOWN_ERROR if failed for other reasons.
*/
- setLnb(ILnb lnb) generates (Result result);
+ setLnb(LnbId lnbId) generates (Result result);
/**
- * Enble or Disable Low Noise Amplifier (LNA).
+ * Enable or Disable Low Noise Amplifier (LNA).
*
* @param bEnable true if activate LNA module; false if deactivate LNA
*
@@ -158,22 +158,4 @@
* UNKNOWN_ERROR if failed for other reasons.
*/
setLna(bool bEnable) generates (Result result);
-
- /**
- * Sends DiSEqC (Digital Satellite Equipment Control) message.
- *
- * Client sends DiSeqc message to DiSEqc compatible device through the
- * frontend. The response message from the device comes back to the client
- * through frontend's callback onDiseqcMessage.
- *
- * @param diseqcMessage a byte array of data for DiSEqC message which is
- * specified by EUTELSAT Bus Functional Specification Version 4.2.
- *
- * @return result Result status of the operation.
- * SUCCESS if successful,
- * INVALID_STATE if the frontend can't send DiSEqc Message, such as
- * cable frontend.
- * UNKNOWN_ERROR if failed for other reasons.
- */
- sendDiseqcMessage(vec<uint8_t> diseqcMessage) generates (Result result);
};
diff --git a/tv/tuner/1.0/ILnb.hal b/tv/tuner/1.0/ILnb.hal
index 49fc3b4..6b7119e 100644
--- a/tv/tuner/1.0/ILnb.hal
+++ b/tv/tuner/1.0/ILnb.hal
@@ -55,6 +55,24 @@
setSatellitePosition(FrontendLnbPosition position) generates (Result result);
/**
+ * Sends DiSEqC (Digital Satellite Equipment Control) message.
+ *
+ * Client sends DiSeqc message to DiSEqc to LNB. The response message from
+ * the device comes back to the client through frontend's callback
+ * onDiseqcMessage.
+ *
+ * @param diseqcMessage a byte array of data for DiSEqC message which is
+ * specified by EUTELSAT Bus Functional Specification Version 4.2.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if the frontend can't send DiSEqc Message, such as
+ * cable frontend.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ sendDiseqcMessage(vec<uint8_t> diseqcMessage) generates (Result result);
+
+ /**
* Releases the LNB instance
*
* Associated resources are released. close may be called more than once.
diff --git a/tv/tuner/1.0/ITuner.hal b/tv/tuner/1.0/ITuner.hal
index f1a8617..1cf0e38 100644
--- a/tv/tuner/1.0/ITuner.hal
+++ b/tv/tuner/1.0/ITuner.hal
@@ -23,7 +23,7 @@
/**
* Top level interface to manage Frontend, Demux and Decrambler hardware
- * resouces which are needed for Android TV.
+ * resources which are needed for Android TV.
*/
interface ITuner {
/**
@@ -68,6 +68,16 @@
generates (Result result, DemuxId demuxId, IDemux demux);
/**
+ * Retrieve the Demux's Capabilities.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * UNKNOWN_ERROR if the inquiry failed for other reasons.
+ * @return caps the Demux's Capabilities.
+ */
+ getDemuxCaps() generates (Result result, DemuxCapabilities caps);
+
+ /**
* Create a new instance of Descrambler.
*
* It is used by the client to create a Descrambler instance.
@@ -81,14 +91,13 @@
generates (Result result, IDescrambler descrambler);
/**
- * Create a new instance of Descrambler.
+ * Retrieve the frontend's information.
*
- * It is used by the client to create a Descrambler instance.
- *
+ * @param frontendId the id of the frontend to be inquiried.
* @return result Result status of the operation.
* SUCCESS if successful,
- * UNKNOWN_ERROR if creation failed for other reasons.
- * @return descrambler the newly created descrambler interface.
+ * UNKNOWN_ERROR if the inquiry failed for other reasons.
+ * @return info the frontend's information.
*/
getFrontendInfo(FrontendId frontendId)
generates (Result result, FrontendInfo info);
@@ -119,6 +128,5 @@
*/
openLnbById(LnbId lnbId)
generates (Result result, ILnb lnb);
-
};
diff --git a/tv/tuner/1.0/default/Demux.cpp b/tv/tuner/1.0/default/Demux.cpp
index 889e42e..d65df59 100644
--- a/tv/tuner/1.0/default/Demux.cpp
+++ b/tv/tuner/1.0/default/Demux.cpp
@@ -67,8 +67,9 @@
0x73, 0x63, 0x65, 0x6e, 0x65,
};
-Demux::Demux(uint32_t demuxId) {
+Demux::Demux(uint32_t demuxId, sp<Tuner> tuner) {
mDemuxId = demuxId;
+ mTunerService = tuner;
}
Demux::~Demux() {}
@@ -76,9 +77,20 @@
Return<Result> Demux::setFrontendDataSource(uint32_t frontendId) {
ALOGV("%s", __FUNCTION__);
- mSourceFrontendId = frontendId;
+ if (mTunerService == nullptr) {
+ return Result::NOT_INITIALIZED;
+ }
- return Result::SUCCESS;
+ mFrontend = mTunerService->getFrontendById(frontendId);
+
+ if (mFrontend == nullptr) {
+ return Result::INVALID_STATE;
+ }
+
+ mFrontendSourceFile = mFrontend->getSourceFile();
+
+ mTunerService->setFrontendAsDemuxSource(frontendId, mDemuxId);
+ return startBroadcastInputLoop();
}
Return<void> Demux::addFilter(DemuxFilterType type, uint32_t bufferSize,
@@ -100,6 +112,8 @@
mFilterEventFlags.resize(filterId + 1);
mFilterThreadRunning.resize(filterId + 1);
mFilterThreads.resize(filterId + 1);
+ mFilterPids.resize(filterId + 1);
+ mFilterOutputs.resize(filterId + 1);
}
mUsedFilterIds.insert(filterId);
@@ -142,10 +156,34 @@
return Void();
}
-Return<Result> Demux::configureFilter(uint32_t /* filterId */,
- const DemuxFilterSettings& /* settings */) {
+Return<Result> Demux::configureFilter(uint32_t filterId, const DemuxFilterSettings& settings) {
ALOGV("%s", __FUNCTION__);
+ switch (mFilterEvents[filterId].filterType) {
+ case DemuxFilterType::SECTION:
+ mFilterPids[filterId] = settings.section().tpid;
+ break;
+ case DemuxFilterType::PES:
+ mFilterPids[filterId] = settings.pesData().tpid;
+ break;
+ case DemuxFilterType::TS:
+ mFilterPids[filterId] = settings.ts().tpid;
+ break;
+ case DemuxFilterType::AUDIO:
+ mFilterPids[filterId] = settings.audio().tpid;
+ break;
+ case DemuxFilterType::VIDEO:
+ mFilterPids[filterId] = settings.video().tpid;
+ break;
+ case DemuxFilterType::RECORD:
+ mFilterPids[filterId] = settings.record().tpid;
+ break;
+ case DemuxFilterType::PCR:
+ mFilterPids[filterId] = settings.pcr().tpid;
+ break;
+ default:
+ return Result::UNKNOWN_ERROR;
+ }
return Result::SUCCESS;
}
@@ -158,36 +196,18 @@
return Result::INVALID_ARGUMENT;
}
- switch (mFilterEvents[filterId].filterType) {
- case DemuxFilterType::SECTION:
- result = startFilterLoop(filterId);
- break;
- case DemuxFilterType::PES:
- result = startPesFilterHandler(filterId);
- break;
- case DemuxFilterType::TS:
- result = startTsFilterHandler();
- return Result::SUCCESS;
- case DemuxFilterType::AUDIO:
- case DemuxFilterType::VIDEO:
- result = startMediaFilterHandler(filterId);
- break;
- case DemuxFilterType::RECORD:
- result = startRecordFilterHandler(filterId);
- break;
- case DemuxFilterType::PCR:
- result = startPcrFilterHandler();
- return Result::SUCCESS;
- default:
- return Result::UNKNOWN_ERROR;
- }
+ result = startFilterLoop(filterId);
return result;
}
-Return<Result> Demux::stopFilter(uint32_t /* filterId */) {
+Return<Result> Demux::stopFilter(uint32_t filterId) {
ALOGV("%s", __FUNCTION__);
+ mFilterThreadRunning[filterId] = false;
+
+ std::lock_guard<std::mutex> lock(mFilterThreadLock);
+
return Result::SUCCESS;
}
@@ -238,6 +258,8 @@
mFilterMQs.clear();
mFilterEvents.clear();
mFilterEventFlags.clear();
+ mFilterOutputs.clear();
+ mFilterPids.clear();
mLastUsedFilterId = -1;
return Result::SUCCESS;
@@ -277,19 +299,21 @@
return Void();
}
-Return<Result> Demux::configureOutput(const DemuxOutputSettings& /* settings */) {
+Return<Result> Demux::configureOutput(const DemuxOutputSettings& settings) {
+ ALOGV("%s", __FUNCTION__);
+
+ mOutputConfigured = true;
+ mOutputSettings = settings;
+ return Result::SUCCESS;
+}
+
+Return<Result> Demux::attachOutputFilter(uint32_t /*filterId*/) {
ALOGV("%s", __FUNCTION__);
return Result::SUCCESS;
}
-Return<Result> Demux::attachOutputTsFilter(uint32_t /*filterId*/) {
- ALOGV("%s", __FUNCTION__);
-
- return Result::SUCCESS;
-}
-
-Return<Result> Demux::detachOutputTsFilter(uint32_t /* filterId */) {
+Return<Result> Demux::detachOutputFilter(uint32_t /* filterId */) {
ALOGV("%s", __FUNCTION__);
return Result::SUCCESS;
@@ -353,15 +377,26 @@
return Void();
}
-Return<Result> Demux::configureInput(const DemuxInputSettings& /* settings */) {
+Return<Result> Demux::configureInput(const DemuxInputSettings& settings) {
ALOGV("%s", __FUNCTION__);
+ mInputConfigured = true;
+ mInputSettings = settings;
+
return Result::SUCCESS;
}
Return<Result> Demux::startInput() {
ALOGV("%s", __FUNCTION__);
+ if (!mInputCallback) {
+ return Result::NOT_INITIALIZED;
+ }
+
+ if (!mInputConfigured) {
+ return Result::INVALID_STATE;
+ }
+
pthread_create(&mInputThread, NULL, __threadLoopInput, this);
pthread_setname_np(mInputThread, "demux_input_waiting_loop");
@@ -373,6 +408,10 @@
Return<Result> Demux::stopInput() {
ALOGV("%s", __FUNCTION__);
+ mInputThreadRunning = false;
+
+ std::lock_guard<std::mutex> lock(mInputThreadLock);
+
return Result::SUCCESS;
}
@@ -403,36 +442,52 @@
return Result::SUCCESS;
}
-Result Demux::startSectionFilterHandler(uint32_t filterId, vector<uint8_t> data) {
- if (!writeSectionsAndCreateEvent(filterId, data)) {
+Result Demux::startSectionFilterHandler(uint32_t filterId) {
+ if (mFilterOutputs[filterId].empty()) {
+ return Result::SUCCESS;
+ }
+ if (!writeSectionsAndCreateEvent(filterId, mFilterOutputs[filterId])) {
ALOGD("[Demux] filter %d fails to write into FMQ. Ending thread", filterId);
return Result::UNKNOWN_ERROR;
}
+ mFilterOutputs[filterId].clear();
+
return Result::SUCCESS;
}
Result Demux::startPesFilterHandler(uint32_t filterId) {
- // TODO generate multiple events in one event callback
+ std::lock_guard<std::mutex> lock(mFilterEventLock);
DemuxFilterPesEvent pesEvent;
- pesEvent = {
- // temp dump meta data
- .streamId = 0,
- .dataLength = 530,
- };
- mFilterEvents[filterId].events.resize(1);
- mFilterEvents[filterId].events[0].pes(pesEvent);
- /*pthread_create(&mThreadId, NULL, __threadLoop, this);
- pthread_setname_np(mThreadId, "demux_section_filter_waiting_loop");*/
- if (!writeDataToFilterMQ(fakeDataInputBuffer, filterId)) {
- return Result::INVALID_STATE;
+ if (mFilterOutputs[filterId].empty()) {
+ return Result::SUCCESS;
}
- if (mDemuxCallbacks[filterId] == nullptr) {
- return Result::NOT_INITIALIZED;
+ for (int i = 0; i < mFilterOutputs[filterId].size(); i += 188) {
+ uint8_t pusi = mFilterOutputs[filterId][i + 1] & 0x40;
+ uint8_t adaptFieldControl = (mFilterOutputs[filterId][i + 3] & 0x30) >> 4;
+ ALOGD("[Demux] pusi %d, adaptFieldControl %d", pusi, adaptFieldControl);
+ if (pusi && (adaptFieldControl == 0x01)) {
+ vector<uint8_t>::const_iterator first = mFilterOutputs[filterId].begin() + i + 4;
+ vector<uint8_t>::const_iterator last = mFilterOutputs[filterId].begin() + i + 187;
+ vector<uint8_t> filterOutData(first, last);
+ if (!writeDataToFilterMQ(filterOutData, filterId)) {
+ mFilterOutputs[filterId].clear();
+ return Result::INVALID_STATE;
+ }
+ pesEvent = {
+ // temp dump meta data
+ .streamId = filterOutData[3],
+ .dataLength = static_cast<uint16_t>(filterOutData.size()),
+ };
+ int size = mFilterEvents[filterId].events.size();
+ mFilterEvents[filterId].events.resize(size + 1);
+ mFilterEvents[filterId].events[size].pes(pesEvent);
+ }
}
- mDemuxCallbacks[filterId]->onFilterEvent(mFilterEvents[filterId]);
+ mFilterOutputs[filterId].clear();
+
return Result::SUCCESS;
}
@@ -451,6 +506,8 @@
};
mFilterEvents[filterId].events.resize(1);
mFilterEvents[filterId].events[0].media() = mediaEvent;
+
+ mFilterOutputs[filterId].clear();
// TODO handle write FQM for media stream
return Result::SUCCESS;
}
@@ -465,6 +522,8 @@
recordEvent.indexMask.tsIndexMask() = 0x01;
mFilterEvents[filterId].events.resize(1);
mFilterEvents[filterId].events[0].ts() = recordEvent;
+
+ mFilterOutputs[filterId].clear();
return Result::SUCCESS;
}
@@ -499,18 +558,18 @@
bool Demux::writeSectionsAndCreateEvent(uint32_t filterId, vector<uint8_t> data) {
// TODO check how many sections has been read
std::lock_guard<std::mutex> lock(mFilterEventLock);
- int size = mFilterEvents[filterId].events.size();
- mFilterEvents[filterId].events.resize(size + 1);
if (!writeDataToFilterMQ(data, filterId)) {
return false;
}
+ int size = mFilterEvents[filterId].events.size();
+ mFilterEvents[filterId].events.resize(size + 1);
DemuxFilterSectionEvent secEvent;
secEvent = {
// temp dump meta data
.tableId = 0,
.version = 1,
.sectionNum = 1,
- .dataLength = 530,
+ .dataLength = static_cast<uint16_t>(data.size()),
};
mFilterEvents[filterId].events[size].section(secEvent);
return true;
@@ -524,21 +583,44 @@
return false;
}
-bool Demux::filterAndOutputData() {
- ALOGD("[Demux] start to dispatch data to filters");
+bool Demux::readInputFMQ() {
// Read input data from the input FMQ
int size = mInputMQ->availableToRead();
+ int inputPacketSize = mInputSettings.packetSize;
vector<uint8_t> dataOutputBuffer;
- dataOutputBuffer.resize(size);
- mInputMQ->read(dataOutputBuffer.data(), size);
+ dataOutputBuffer.resize(inputPacketSize);
- Result result;
- // Filter the data and feed the output to each filter
+ // Dispatch the packet to the PID matching filter output buffer
+ for (int i = 0; i < size / inputPacketSize; i++) {
+ if (!mInputMQ->read(dataOutputBuffer.data(), inputPacketSize)) {
+ return false;
+ }
+ startTsFilter(dataOutputBuffer);
+ }
+
+ return true;
+}
+
+void Demux::startTsFilter(vector<uint8_t> data) {
set<uint32_t>::iterator it;
for (it = mUsedFilterIds.begin(); it != mUsedFilterIds.end(); it++) {
+ uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff));
+ ALOGW("start ts filter pid: %d", pid);
+ if (pid == mFilterPids[*it]) {
+ mFilterOutputs[*it].insert(mFilterOutputs[*it].end(), data.begin(), data.end());
+ }
+ }
+}
+
+bool Demux::startFilterDispatcher() {
+ Result result;
+ set<uint32_t>::iterator it;
+
+ // Handle the output data per filter type
+ for (it = mUsedFilterIds.begin(); it != mUsedFilterIds.end(); it++) {
switch (mFilterEvents[*it].filterType) {
case DemuxFilterType::SECTION:
- result = startSectionFilterHandler(*it, dataOutputBuffer);
+ result = startSectionFilterHandler(*it);
break;
case DemuxFilterType::PES:
result = startPesFilterHandler(*it);
@@ -578,6 +660,7 @@
void Demux::filterThreadLoop(uint32_t filterId) {
ALOGD("[Demux] filter %d threadLoop start.", filterId);
+ std::lock_guard<std::mutex> lock(mFilterThreadLock);
mFilterThreadRunning[filterId] = true;
// For the first time of filter output, implementation needs to send the filter
@@ -640,6 +723,7 @@
void Demux::inputThreadLoop() {
ALOGD("[Demux] input threadLoop start.");
+ std::lock_guard<std::mutex> lock(mInputThreadLock);
mInputThreadRunning = true;
while (mInputThreadRunning) {
@@ -651,18 +735,112 @@
ALOGD("[Demux] wait for data ready on the input FMQ");
continue;
}
- // Our current implementation filter the data and write it into the filter FMQ immedaitely
+ // Our current implementation filter the data and write it into the filter FMQ immediately
// after the DATA_READY from the VTS/framework
- if (!filterAndOutputData()) {
+ if (!readInputFMQ() || !startFilterDispatcher()) {
ALOGD("[Demux] input data failed to be filtered. Ending thread");
break;
}
+
+ maySendInputStatusCallback();
}
mInputThreadRunning = false;
ALOGD("[Demux] input thread ended.");
}
+void Demux::maySendInputStatusCallback() {
+ std::lock_guard<std::mutex> lock(mInputStatusLock);
+ int availableToRead = mInputMQ->availableToRead();
+ int availableToWrite = mInputMQ->availableToWrite();
+
+ DemuxInputStatus newStatus =
+ checkStatusChange(availableToWrite, availableToRead, mInputSettings.highThreshold,
+ mInputSettings.lowThreshold);
+ if (mIntputStatus != newStatus) {
+ mInputCallback->onInputStatus(newStatus);
+ mIntputStatus = newStatus;
+ }
+}
+
+DemuxInputStatus Demux::checkStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
+ uint32_t highThreshold, uint32_t lowThreshold) {
+ if (availableToWrite == 0) {
+ return DemuxInputStatus::SPACE_FULL;
+ } else if (availableToRead > highThreshold) {
+ return DemuxInputStatus::SPACE_ALMOST_FULL;
+ } else if (availableToRead < lowThreshold) {
+ return DemuxInputStatus::SPACE_ALMOST_EMPTY;
+ } else if (availableToRead == 0) {
+ return DemuxInputStatus::SPACE_EMPTY;
+ }
+ return mIntputStatus;
+}
+
+Result Demux::startBroadcastInputLoop() {
+ pthread_create(&mBroadcastInputThread, NULL, __threadLoopBroadcast, this);
+ pthread_setname_np(mBroadcastInputThread, "broadcast_input_thread");
+
+ return Result::SUCCESS;
+}
+
+void* Demux::__threadLoopBroadcast(void* user) {
+ Demux* const self = static_cast<Demux*>(user);
+ self->broadcastInputThreadLoop();
+ return 0;
+}
+
+void Demux::broadcastInputThreadLoop() {
+ std::lock_guard<std::mutex> lock(mBroadcastInputThreadLock);
+ mBroadcastInputThreadRunning = true;
+ mKeepFetchingDataFromFrontend = true;
+
+ // open the stream and get its length
+ std::ifstream inputData(mFrontendSourceFile, std::ifstream::binary);
+ // TODO take the packet size from the frontend setting
+ int packetSize = 188;
+ int writePacketAmount = 6;
+ char* buffer = new char[packetSize];
+ ALOGW("[Demux] broadcast input thread loop start %s", mFrontendSourceFile.c_str());
+ if (!inputData.is_open()) {
+ mBroadcastInputThreadRunning = false;
+ ALOGW("[Demux] Error %s", strerror(errno));
+ }
+
+ while (mBroadcastInputThreadRunning) {
+ // move the stream pointer for packet size * 6 every read until the end
+ while (mKeepFetchingDataFromFrontend) {
+ for (int i = 0; i < writePacketAmount; i++) {
+ inputData.read(buffer, packetSize);
+ if (!inputData) {
+ mBroadcastInputThreadRunning = false;
+ break;
+ }
+ // filter and dispatch filter output
+ vector<uint8_t> byteBuffer;
+ byteBuffer.resize(sizeof(buffer));
+ for (int index = 0; index < byteBuffer.size(); index++) {
+ byteBuffer[index] = static_cast<uint8_t>(buffer[index]);
+ }
+ startTsFilter(byteBuffer);
+ inputData.seekg(packetSize, inputData.cur);
+ }
+ startFilterDispatcher();
+ sleep(1);
+ }
+ }
+
+ ALOGW("[Demux] Broadcast Input thread end.");
+ delete[] buffer;
+ inputData.close();
+}
+
+void Demux::stopBroadcastInput() {
+ mKeepFetchingDataFromFrontend = false;
+ mBroadcastInputThreadRunning = false;
+ std::lock_guard<std::mutex> lock(mBroadcastInputThreadLock);
+}
+
} // namespace implementation
} // namespace V1_0
} // namespace tuner
diff --git a/tv/tuner/1.0/default/Demux.h b/tv/tuner/1.0/default/Demux.h
index 2fdde8d..e4a4e2b 100644
--- a/tv/tuner/1.0/default/Demux.h
+++ b/tv/tuner/1.0/default/Demux.h
@@ -20,6 +20,8 @@
#include <android/hardware/tv/tuner/1.0/IDemux.h>
#include <fmq/MessageQueue.h>
#include <set>
+#include "Frontend.h"
+#include "Tuner.h"
using namespace std;
@@ -40,9 +42,12 @@
using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+class Tuner;
+class Frontend;
+
class Demux : public IDemux {
public:
- Demux(uint32_t demuxId);
+ Demux(uint32_t demuxId, sp<Tuner> tuner);
~Demux();
@@ -91,9 +96,9 @@
virtual Return<Result> configureOutput(const DemuxOutputSettings& settings) override;
- virtual Return<Result> attachOutputTsFilter(uint32_t filterId) override;
+ virtual Return<Result> attachOutputFilter(uint32_t filterId) override;
- virtual Return<Result> detachOutputTsFilter(uint32_t filterId) override;
+ virtual Return<Result> detachOutputFilter(uint32_t filterId) override;
virtual Return<Result> startOutput() override;
@@ -103,7 +108,17 @@
virtual Return<Result> removeOutput() override;
+ // Functions interacts with Tuner Service
+ void stopBroadcastInput();
+
private:
+ // Tuner service
+ sp<Tuner> mTunerService;
+
+ // Frontend source
+ sp<Frontend> mFrontend;
+ string mFrontendSourceFile;
+
// A struct that passes the arguments to a newly created filter thread
struct ThreadArgs {
Demux* user;
@@ -115,13 +130,14 @@
* They are also responsible to write the filtered output into the filter FMQ
* and update the filterEvent bound with the same filterId.
*/
- Result startSectionFilterHandler(uint32_t filterId, vector<uint8_t> data);
+ Result startSectionFilterHandler(uint32_t filterId);
Result startPesFilterHandler(uint32_t filterId);
Result startTsFilterHandler();
Result startMediaFilterHandler(uint32_t filterId);
Result startRecordFilterHandler(uint32_t filterId);
Result startPcrFilterHandler();
Result startFilterLoop(uint32_t filterId);
+ Result startBroadcastInputLoop();
/**
* To create a FilterMQ with the the next available Filter ID.
@@ -136,18 +152,24 @@
bool writeDataToFilterMQ(const std::vector<uint8_t>& data, uint32_t filterId);
bool readDataFromMQ();
bool writeSectionsAndCreateEvent(uint32_t filterId, vector<uint8_t> data);
+ void maySendInputStatusCallback();
+ DemuxInputStatus checkStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
+ uint32_t highThreshold, uint32_t lowThreshold);
/**
* A dispatcher to read and dispatch input data to all the started filters.
* Each filter handler handles the data filtering/output writing/filterEvent updating.
*/
- bool filterAndOutputData();
+ bool readInputFMQ();
+ void startTsFilter(vector<uint8_t> data);
+ bool startFilterDispatcher();
static void* __threadLoopFilter(void* data);
static void* __threadLoopInput(void* user);
+ static void* __threadLoopBroadcast(void* user);
void filterThreadLoop(uint32_t filterId);
void inputThreadLoop();
+ void broadcastInputThreadLoop();
uint32_t mDemuxId;
- uint32_t mSourceFrontendId;
/**
* Record the last used filter id. Initial value is -1.
* Filter Id starts with 0.
@@ -169,6 +191,8 @@
* A list of created FilterMQ ptrs.
* The array number is the filter ID.
*/
+ vector<uint16_t> mFilterPids;
+ vector<vector<uint8_t>> mFilterOutputs;
vector<unique_ptr<FilterMQ>> mFilterMQs;
vector<EventFlag*> mFilterEventFlags;
vector<DemuxFilterEvent> mFilterEvents;
@@ -182,15 +206,26 @@
vector<sp<IDemuxCallback>> mDemuxCallbacks;
sp<IDemuxCallback> mInputCallback;
sp<IDemuxCallback> mOutputCallback;
+ bool mInputConfigured = false;
+ bool mOutputConfigured = false;
+ DemuxInputSettings mInputSettings;
+ DemuxOutputSettings mOutputSettings;
+
// Thread handlers
pthread_t mInputThread;
pthread_t mOutputThread;
+ pthread_t mBroadcastInputThread;
vector<pthread_t> mFilterThreads;
+
+ // FMQ status local records
+ DemuxInputStatus mIntputStatus;
/**
* If a specific filter's writing loop is still running
*/
vector<bool> mFilterThreadRunning;
bool mInputThreadRunning;
+ bool mBroadcastInputThreadRunning;
+ bool mKeepFetchingDataFromFrontend;
/**
* Lock to protect writes to the FMQs
*/
@@ -198,8 +233,16 @@
/**
* Lock to protect writes to the filter event
*/
+ // TODO make each filter separate event lock
std::mutex mFilterEventLock;
/**
+ * Lock to protect writes to the input status
+ */
+ std::mutex mInputStatusLock;
+ std::mutex mBroadcastInputThreadLock;
+ std::mutex mFilterThreadLock;
+ std::mutex mInputThreadLock;
+ /**
* How many times a filter should write
* TODO make this dynamic/random/can take as a parameter
*/
diff --git a/tv/tuner/1.0/default/Frontend.cpp b/tv/tuner/1.0/default/Frontend.cpp
index 0609d05..1e07edd 100644
--- a/tv/tuner/1.0/default/Frontend.cpp
+++ b/tv/tuner/1.0/default/Frontend.cpp
@@ -27,14 +27,10 @@
namespace V1_0 {
namespace implementation {
-Frontend::Frontend() {
- // Init callback to nullptr
- mCallback = nullptr;
-}
-
-Frontend::Frontend(FrontendType type, FrontendId id) {
+Frontend::Frontend(FrontendType type, FrontendId id, sp<Tuner> tuner) {
mType = type;
mId = id;
+ mTunerService = tuner;
// Init callback to nullptr
mCallback = nullptr;
}
@@ -67,13 +63,18 @@
return Result::INVALID_STATE;
}
- mCallback->onEvent(FrontendEventType::NO_SIGNAL);
+ // TODO dynamically allocate file to the source file
+ mSourceStreamFile = FRONTEND_STREAM_FILE;
+
+ mCallback->onEvent(FrontendEventType::LOCKED);
return Result::SUCCESS;
}
Return<Result> Frontend::stopTune() {
ALOGV("%s", __FUNCTION__);
+ mTunerService->frontendStopTune(mId);
+
return Result::SUCCESS;
}
@@ -105,13 +106,7 @@
return Result::SUCCESS;
}
-Return<Result> Frontend::setLnb(const sp<ILnb>& /* lnb */) {
- ALOGV("%s", __FUNCTION__);
-
- return Result::SUCCESS;
-}
-
-Return<Result> Frontend::sendDiseqcMessage(const hidl_vec<uint8_t>& /* diseqcMessage */) {
+Return<Result> Frontend::setLnb(uint32_t /* lnb */) {
ALOGV("%s", __FUNCTION__);
return Result::SUCCESS;
@@ -125,6 +120,10 @@
return mId;
}
+string Frontend::getSourceFile() {
+ return mSourceStreamFile;
+}
+
} // namespace implementation
} // namespace V1_0
} // namespace tuner
diff --git a/tv/tuner/1.0/default/Frontend.h b/tv/tuner/1.0/default/Frontend.h
index fc586b5..07fa7b9 100644
--- a/tv/tuner/1.0/default/Frontend.h
+++ b/tv/tuner/1.0/default/Frontend.h
@@ -18,7 +18,9 @@
#define ANDROID_HARDWARE_TV_TUNER_V1_0_FRONTEND_H_
#include <android/hardware/tv/tuner/1.0/IFrontend.h>
-#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <fstream>
+#include <iostream>
+#include "Tuner.h"
using namespace std;
@@ -35,11 +37,11 @@
using ::android::hardware::tv::tuner::V1_0::IFrontendCallback;
using ::android::hardware::tv::tuner::V1_0::Result;
+class Tuner;
+
class Frontend : public IFrontend {
public:
- Frontend();
-
- Frontend(FrontendType type, FrontendId id);
+ Frontend(FrontendType type, FrontendId id, sp<Tuner> tuner);
virtual Return<Result> close() override;
@@ -56,21 +58,26 @@
virtual Return<void> getStatus(const hidl_vec<FrontendStatusType>& statusTypes,
getStatus_cb _hidl_cb) override;
- virtual Return<Result> sendDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) override;
-
virtual Return<Result> setLna(bool bEnable) override;
- virtual Return<Result> setLnb(const sp<ILnb>& lnb) override;
+ virtual Return<Result> setLnb(uint32_t lnb) override;
FrontendType getFrontendType();
FrontendId getFrontendId();
+ string getSourceFile();
+
private:
virtual ~Frontend();
sp<IFrontendCallback> mCallback;
+ sp<Tuner> mTunerService;
FrontendType mType = FrontendType::UNDEFINED;
FrontendId mId = 0;
+
+ const string FRONTEND_STREAM_FILE = "/vendor/etc/test1.ts";
+ string mSourceStreamFile;
+ std::ifstream mFrontendData;
};
} // namespace implementation
diff --git a/tv/tuner/1.0/default/Lnb.cpp b/tv/tuner/1.0/default/Lnb.cpp
index b81bb15..1446f7f 100644
--- a/tv/tuner/1.0/default/Lnb.cpp
+++ b/tv/tuner/1.0/default/Lnb.cpp
@@ -48,6 +48,12 @@
return Result::SUCCESS;
}
+Return<Result> Lnb::sendDiseqcMessage(const hidl_vec<uint8_t>& /* diseqcMessage */) {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
Return<Result> Lnb::close() {
ALOGV("%s", __FUNCTION__);
diff --git a/tv/tuner/1.0/default/Lnb.h b/tv/tuner/1.0/default/Lnb.h
index df7e0fe..4c251f7 100644
--- a/tv/tuner/1.0/default/Lnb.h
+++ b/tv/tuner/1.0/default/Lnb.h
@@ -38,12 +38,14 @@
public:
Lnb();
- virtual Return<Result> setVoltage(FrontendLnbVoltage voltage);
+ virtual Return<Result> setVoltage(FrontendLnbVoltage voltage) override;
virtual Return<Result> setTone(FrontendLnbTone tone) override;
virtual Return<Result> setSatellitePosition(FrontendLnbPosition position) override;
+ virtual Return<Result> sendDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) override;
+
virtual Return<Result> close() override;
private:
diff --git a/tv/tuner/1.0/default/Tuner.cpp b/tv/tuner/1.0/default/Tuner.cpp
index 00831ae..f86b28d 100644
--- a/tv/tuner/1.0/default/Tuner.cpp
+++ b/tv/tuner/1.0/default/Tuner.cpp
@@ -38,14 +38,14 @@
// Array index matches their FrontendId in the default impl
mFrontendSize = 8;
mFrontends.resize(mFrontendSize);
- mFrontends[0] = new Frontend();
- mFrontends[1] = new Frontend(FrontendType::ATSC, 1);
- mFrontends[2] = new Frontend(FrontendType::DVBC, 2);
- mFrontends[3] = new Frontend(FrontendType::DVBS, 3);
- mFrontends[4] = new Frontend(FrontendType::DVBT, 4);
- mFrontends[5] = new Frontend(FrontendType::ISDBT, 5);
- mFrontends[6] = new Frontend(FrontendType::ANALOG, 6);
- mFrontends[7] = new Frontend(FrontendType::ATSC, 7);
+ mFrontends[0] = new Frontend(FrontendType::DVBT, 0, this);
+ mFrontends[1] = new Frontend(FrontendType::ATSC, 1, this);
+ mFrontends[2] = new Frontend(FrontendType::DVBC, 2, this);
+ mFrontends[3] = new Frontend(FrontendType::DVBS, 3, this);
+ mFrontends[4] = new Frontend(FrontendType::DVBT, 4, this);
+ mFrontends[5] = new Frontend(FrontendType::ISDBT, 5, this);
+ mFrontends[6] = new Frontend(FrontendType::ANALOG, 6, this);
+ mFrontends[7] = new Frontend(FrontendType::ATSC, 7, this);
}
Tuner::~Tuner() {}
@@ -81,12 +81,22 @@
DemuxId demuxId = mLastUsedId + 1;
mLastUsedId += 1;
- sp<IDemux> demux = new Demux(demuxId);
+ sp<Demux> demux = new Demux(demuxId, this);
+ mDemuxes[demuxId] = demux;
_hidl_cb(Result::SUCCESS, demuxId, demux);
return Void();
}
+Return<void> Tuner::getDemuxCaps(getDemuxCaps_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ DemuxCapabilities caps;
+
+ _hidl_cb(Result::SUCCESS, caps);
+ return Void();
+}
+
Return<void> Tuner::openDescrambler(openDescrambler_cb _hidl_cb) {
ALOGV("%s", __FUNCTION__);
@@ -123,6 +133,25 @@
return Void();
}
+sp<Frontend> Tuner::getFrontendById(uint32_t frontendId) {
+ ALOGV("%s", __FUNCTION__);
+
+ return mFrontends[frontendId];
+}
+
+void Tuner::setFrontendAsDemuxSource(uint32_t frontendId, uint32_t demuxId) {
+ mFrontendToDemux[frontendId] = demuxId;
+}
+
+void Tuner::frontendStopTune(uint32_t frontendId) {
+ map<uint32_t, uint32_t>::iterator it = mFrontendToDemux.find(frontendId);
+ uint32_t demuxId;
+ if (it != mFrontendToDemux.end()) {
+ demuxId = it->second;
+ mDemuxes[demuxId]->stopBroadcastInput();
+ }
+}
+
} // namespace implementation
} // namespace V1_0
} // namespace tuner
diff --git a/tv/tuner/1.0/default/Tuner.h b/tv/tuner/1.0/default/Tuner.h
index 62227ee..96da257 100644
--- a/tv/tuner/1.0/default/Tuner.h
+++ b/tv/tuner/1.0/default/Tuner.h
@@ -18,6 +18,8 @@
#define ANDROID_HARDWARE_TV_TUNER_V1_0_TUNER_H_
#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <map>
+#include "Demux.h"
#include "Frontend.h"
using namespace std;
@@ -29,6 +31,9 @@
namespace V1_0 {
namespace implementation {
+class Frontend;
+class Demux;
+
class Tuner : public ITuner {
public:
Tuner();
@@ -39,6 +44,8 @@
virtual Return<void> openDemux(openDemux_cb _hidl_cb) override;
+ virtual Return<void> getDemuxCaps(getDemuxCaps_cb _hidl_cb) override;
+
virtual Return<void> openDescrambler(openDescrambler_cb _hidl_cb) override;
virtual Return<void> getFrontendInfo(FrontendId frontendId,
@@ -48,10 +55,18 @@
virtual Return<void> openLnbById(LnbId lnbId, openLnbById_cb _hidl_cb) override;
+ sp<Frontend> getFrontendById(uint32_t frontendId);
+
+ void setFrontendAsDemuxSource(uint32_t frontendId, uint32_t demuxId);
+
+ void frontendStopTune(uint32_t frontendId);
+
private:
virtual ~Tuner();
// Static mFrontends array to maintain local frontends information
vector<sp<Frontend>> mFrontends;
+ std::map<uint32_t, uint32_t> mFrontendToDemux;
+ std::map<uint32_t, sp<Demux>> mDemuxes;
// To maintain how many Frontends we have
int mFrontendSize;
// The last used demux id. Initial value is -1.
diff --git a/tv/tuner/1.0/types.hal b/tv/tuner/1.0/types.hal
index d37f63a..890c1ed 100644
--- a/tv/tuner/1.0/types.hal
+++ b/tv/tuner/1.0/types.hal
@@ -43,7 +43,7 @@
ANALOG,
/* Advanced Television Systems Committee (ATSC) Standard A/72. */
ATSC,
- /* Advanced Television Systems Committee (ATSC 3.0) Standard A/330. */
+ /* Advanced Television Systems Committee (ATSC 3.0) Standard A/300. */
ATSC3,
/**
* Digital Video Broadcasting - Cable
@@ -58,16 +58,16 @@
DVBS,
/**
* Digital Video Broadcasting - Terrestrial
- * DVB Terresttrial Frontend Standard ETSI EN 300 468 V1.15.1 and
+ * DVB Terrestrial Frontend Standard ETSI EN 300 468 V1.15.1 and
* ETSI EN 302 755 V1.4.1.
*/
DVBT,
/* Integrated Services Digital Broadcasting-Satellite (ISDB-S)
- * ARIB SDT-B20 is technical document of ISDB-S.
+ * ARIB STD-B20 is technical document of ISDB-S.
*/
ISDBS,
/* Integrated Services Digital Broadcasting-Satellite (ISDB-S)
- * ARIB TR-B15 is technical document of ISDB-S3.
+ * ARIB STD-B44 is technical document of ISDB-S3.
*/
ISDBS3,
/* Integrated Services Digital Broadcasting-Terrestrial (ISDB-T or SBTVD)
@@ -164,8 +164,10 @@
@export
enum FrontendAtscModulation : uint32_t {
UNDEFINED = 0,
- MOD_8VSB = 1 << 0,
- MOD_16VSB = 1 << 1,
+ /** hardware is able to detect and set modulation automatically */
+ AUTO = 1 << 0,
+ MOD_8VSB = 1 << 2,
+ MOD_16VSB = 1 << 3,
};
/**
@@ -191,12 +193,14 @@
@export
enum FrontendAtsc3Modulation : uint32_t {
UNDEFINED = 0,
- MOD_QPSK = 1 << 0,
- MOD_16QAM = 1 << 1,
- MOD_64QAM = 1 << 2,
- MOD_256QAM = 1 << 3,
- MOD_1024QAM = 1 << 4,
- MOD_4096QAM = 1 << 5,
+ /** hardware is able to detect and set modulation automatically */
+ AUTO = 1 << 0,
+ MOD_QPSK = 1 << 1,
+ MOD_16QAM = 1 << 2,
+ MOD_64QAM = 1 << 3,
+ MOD_256QAM = 1 << 4,
+ MOD_1024QAM = 1 << 5,
+ MOD_4096QAM = 1 << 6,
};
/**
@@ -205,9 +209,11 @@
@export
enum FrontendAtsc3Bandwidth : uint32_t {
UNDEFINED = 0,
- BANDWIDTH_8MHZ = 1 << 0,
- BANDWIDTH_7MHZ = 1 << 1,
- BANDWIDTH_6MHZ = 1 << 2,
+ /** hardware is able to detect and set bandwidth automatically */
+ AUTO = 1 << 0,
+ BANDWIDTH_6MHZ = 1 << 1,
+ BANDWIDTH_7MHZ = 1 << 2,
+ BANDWIDTH_8MHZ = 1 << 3,
};
/**
@@ -215,9 +221,11 @@
*/
@export
enum FrontendAtsc3TimeInterleaveMode : uint32_t {
- UNDEFINED,
- CTI,
- HTI,
+ UNDEFINED = 0,
+ /** hardware is able to detect and set TimeInterleaveMode automatically */
+ AUTO = 1 << 0,
+ CTI = 1 << 1,
+ HTI = 1 << 2,
};
/**
@@ -247,13 +255,39 @@
*/
@export
enum FrontendAtsc3Fec : uint32_t {
- UNDEFINED,
- BCH_LDPC_16K,
- BCH_LDPC_64K,
- CRC_LDPC_16K,
- CRC_LDPC_64K,
- LDPC_16K,
- LDPC_64K,
+ UNDEFINED = 0,
+ /** hardware is able to detect and set FEC automatically */
+ AUTO = 1 << 0,
+ BCH_LDPC_16K = 1 << 1,
+ BCH_LDPC_64K = 1 << 2,
+ CRC_LDPC_16K = 1 << 3,
+ CRC_LDPC_64K = 1 << 4,
+ LDPC_16K = 1 << 5,
+ LDPC_64K = 1 << 6,
+};
+
+/**
+ * Demodulator Output Format for an ATSC3 Frontend.
+ */
+@export
+enum FrontendAtsc3DemodOutputFormat : uint8_t {
+ /** Dummy. Scan uses this. */
+ UNDEFINED = 0,
+ /** ALP format. Typically used in US region. */
+ ATSC3_LINKLAYER_PACKET = 1 << 0,
+ /** BaseBand packet format. Typically used in Korea region. */
+ BASEBAND_PACKET = 1 << 1,
+};
+
+/**
+ * PLP basis Signal Settings for an ATSC3 Frontend.
+ */
+struct FrontendAtsc3PlpSettings {
+ uint8_t plpId;
+ FrontendAtsc3Modulation modulation;
+ FrontendAtsc3TimeInterleaveMode interleaveMode;
+ FrontendAtsc3CodeRate codeRate;
+ FrontendAtsc3Fec fec;
};
/**
@@ -262,21 +296,28 @@
struct FrontendAtsc3Settings {
/** Signal frequency in Hertz */
uint32_t frequency;
+ /** Bandwidth of tuning band. */
FrontendAtsc3Bandwidth bandwidth;
- FrontendAtsc3TimeInterleaveMode interleaveMode;
- FrontendAtsc3CodeRate codeRate;
- FrontendAtsc3Fec fec;
- vec<uint8_t> plpIdList;
+ FrontendAtsc3DemodOutputFormat demodOutputFormat;
+ vec<FrontendAtsc3PlpSettings> plpSettings;
};
/**
* Capabilities for ATSC3 Frontend.
*/
struct FrontendAtsc3Capabilities {
- /** Modulation capability */
- bitfield<FrontendAtsc3Modulation> modulationCap;
/** Bandwidth capability */
bitfield<FrontendAtsc3Bandwidth> bandwidthCap;
+ /** Modulation capability */
+ bitfield<FrontendAtsc3Modulation> modulationCap;
+ /** TimeInterleaveMode capability */
+ bitfield<FrontendAtsc3TimeInterleaveMode> timeInterleaveModeCap;
+ /** CodeRate capability */
+ bitfield<FrontendAtsc3CodeRate> codeRateCap;
+ /** FEC capability */
+ bitfield<FrontendAtsc3Fec> fecCap;
+ /** Demodulator Output Format capability */
+ bitfield<FrontendAtsc3DemodOutputFormat> demodOutputFormatCap;
};
/**
@@ -614,7 +655,7 @@
};
/**
- * Modulaltion Type for ISDBS.
+ * Modulation Type for ISDBS.
*/
@export
enum FrontendIsdbsModulation : uint32_t {
@@ -647,7 +688,7 @@
@export
enum FrontendIsdbsStreamIdType : uint32_t {
STREAM_ID,
- RELATIVE_STREAM_ID,
+ RELATIVE_STREAM_NUMBER,
};
/**
@@ -845,6 +886,7 @@
M_EIA_J = 1 << 13,
I_NICAM = 1 << 14,
L_NICAM = 1 << 15,
+ L_PRIME = 1 << 16,
};
/**
@@ -907,17 +949,27 @@
PLP_IDS,
/** Locked group Ids for DVBT2 frontend. */
GROUP_IDS,
- /** Locked the number of the Plps. */
- INPUT_STREAM_IDS,
- /** Locked signal stardard. */
+ /** Stream Ids. */
+ INPUT_STREAM_IDS,
+ /** Locked signal standard. */
STANDARD,
+ /** PLP status in a tuned frequency band for ATSC3 frontend. */
+ ATSC3_PLP_INFO,
+};
+
+/**
+ * ATSC3.0 PLP information for scan
+ */
+struct FrontendScanAtsc3PlpInfo {
+ uint8_t plpId;
+ bool bLlsFlag;
};
/**
* Scan Message for Frontend.
*/
safe_union FrontendScanMessage {
- bool islocked;
+ bool isLocked;
bool isEnd;
/** scan progress percent (0..100) */
uint8_t progressPercent;
@@ -927,11 +979,13 @@
uint32_t symbolRate;
vec<uint8_t> plpIds;
vec<uint8_t> groupIds;
- vec<uint8_t> inputStreamIds;
+ vec<uint16_t> inputStreamIds;
safe_union standard {
FrontendDvbsStandard sStd;
FrontendDvbtStandard tStd;
} std;
+ /** A list of PLP status in a tuned frequency band for ATSC3 frontend. */
+ vec<FrontendScanAtsc3PlpInfo> atsc3PlpInfos;
};
/**
@@ -940,17 +994,17 @@
@export
enum FrontendEventType : uint32_t {
/**
- * If frontend locked the signal which is specified by tune method, HAL sent
+ * If frontend locked the signal which is specified by tune method, HAL sends
* Locked event.
*/
LOCKED,
/**
* If frontend can't locked the signal which is specified by tune method,
- * HAL sent NO_SIGNAL event.
+ * HAL sends NO_SIGNAL event.
*/
NO_SIGNAL,
/**
- * If frontend detect that the locked signal get lost, HAL sent LOST_LOCK
+ * If frontend detect that the locked signal get lost, HAL sends LOST_LOCK
* event.
*/
LOST_LOCK,
@@ -977,15 +1031,15 @@
*/
@export
enum FrontendStatusType : uint32_t {
- /** Lock status for RF or Demod. */
- LOCK,
+ /** Lock status for Demod. */
+ DEMOD_LOCK,
/** Signal to Noise Ratio. */
SNR,
/** Bit Error Ratio. */
BER,
/** Packages Error Ratio. */
PER,
- /** Bit Error Ratio befor FEC. */
+ /** Bit Error Ratio before FEC. */
PRE_BER,
/*
* Signal Quality (0..100). Good data over total data in percent can be
@@ -993,7 +1047,7 @@
*/
SIGNAL_QUALITY,
/** Signal Strength. */
- SIGGAL_STRENGTH,
+ SIGNAL_STRENGTH,
/** Symbol Rate. */
SYMBOL_RATE,
/** Forward Error Correction Type. */
@@ -1008,21 +1062,62 @@
PLP_ID,
/** Status for Emergency Warning Broadcasting System. */
EWBS,
+ /** Automatic Gain Control. */
+ AGC,
+ /** Low Noise Amplifier. */
+ LNA,
+ /** Lock status for stream. */
+ STREAM_LOCK,
+ /** Error status by layer. */
+ LAYER_ERROR,
+ /** CN value by VBER. */
+ VBER_CN,
+ /** CN value by LBER. */
+ LBER_CN,
+ /** CN value by XER. */
+ XER_CN,
+ /** Moduration Error Ratio. */
+ MER,
+ /** Difference between tuning frequency and actual locked frequency. */
+ FREQ_OFFSET,
+ /* Hierarchy for DVBT. */
+ HIERARCHY,
+ /** Lock status for RF. */
+ RF_LOCK,
+ /** PLP information in a frequency band for ATSC3.0 frontend. */
+ ATSC3_PLP_INFO,
};
/**
+ * Status for each tuning PLPs
+ */
+struct FrontendStatusAtsc3PlpInfo {
+ /** PLP Id value. */
+ uint8_t plpId;
+ /** Demod Lock/Unlock status of this particular PLP. */
+ bool isLocked;
+ /** Uncorrectable Error Counts (UEC) of this particular PLP since last tune operation. */
+ uint32_t uec;
+};
+
+
+/**
* Modulation Type for Frontend's status.
*/
safe_union FrontendModulationStatus {
+ FrontendDvbcModulation dvbc;
FrontendDvbsModulation dvbs;
- FrontendAtsc3Modulation atsc3;
+ FrontendIsdbsModulation isdbs;
+ FrontendIsdbs3Modulation isdbs3;
+ FrontendIsdbtModulation isdbt;
};
/**
* The status for Frontend.
*/
safe_union FrontendStatus {
- bool isLocked;
+ /** Lock status for Demod in True/False. */
+ bool isDemodLocked;
/** SNR value measured by 0.001 dB. */
int32_t snr;
/** The number of error bit per 1 billion bits. */
@@ -1043,6 +1138,25 @@
FrontendLnbVoltage lnbVoltage;
uint8_t plpId;
bool isEWBS;
+ /** AGC value is normalized from 0 to 255. */
+ uint8_t agc;
+ bool isLnaOn;
+ bool isStreamLock;
+ vec<bool> isLayerError;
+ /** CN value by VBER measured by 0.001 dB */
+ int32_t vberCn;
+ /** CN value by LBER measured by 0.001 dB */
+ int32_t lberCn;
+ /** CN value by XER measured by 0.001 dB */
+ int32_t xerCn;
+ /** MER value measured by 0.001 dB */
+ int32_t mer;
+ /** Frequency difference in Hertz. */
+ int32_t freqOffset;
+ FrontendDvbtHierarchy hierarchy;
+ bool isRfLocked;
+ /** A list of PLP status for tuned PLPs for ATSC3 frontend. */
+ vec<FrontendStatusAtsc3PlpInfo> plpInfo;
};
/**
@@ -1121,7 +1235,6 @@
POSITION_B,
};
-
/* Demux ID is used to associate with a hardware demux resource. */
typedef uint32_t DemuxId;
@@ -1150,7 +1263,7 @@
*/
AUDIO,
/**
- * A filter to filter Vidoe Metadata out from input stream.
+ * A filter to filter Video Metadata out from input stream.
*/
VIDEO,
/**
@@ -1475,6 +1588,8 @@
PES,
/* Data is Elementary Stream. */
ES,
+ /* Data is TLV (type-length-value) Stream for JP SHV */
+ SHV_TLV,
};
/**
@@ -1534,6 +1649,10 @@
SPACE_FULL = 1 << 3,
};
+/**
+ * The Settings for the demux's input.
+ */
+@export
struct DemuxInputSettings {
/**
* Register for interested status events so that the HAL can send these
@@ -1559,3 +1678,30 @@
*/
uint8_t packetSize;
};
+
+/**
+ * Capabilities for Demux.
+ */
+@export
+struct DemuxCapabilities {
+ /* The number of Demux to be supported. */
+ uint32_t numDemux;
+ /* The number of Input to be supported. */
+ uint32_t numInput;
+ /* The number of Output to be supported. */
+ uint32_t numOutput;
+ /* The number of TS Filter to be supported. */
+ uint32_t numTsFilter;
+ /* The number of Section Filter to be supported. */
+ uint32_t numSectionFilter;
+ /* The number of Audio Filter to be supported. */
+ uint32_t numAudioFilter;
+ /* The number of Video Filter to be supported. */
+ uint32_t numVideoFilter;
+ /* The number of PES Filter to be supported. */
+ uint32_t numPesFilter;
+ /* The number of PCR Filter to be supported. */
+ uint32_t numPcrFilter;
+ /* The maximum number of bytes is supported in the mask of Section Filter. */
+ uint32_t numBytesInSectionFilter;
+};
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index 7256cc4..7936185 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -39,6 +39,7 @@
#include <map>
#define WAIT_TIMEOUT 3000000000
+#define WAIT_TIMEOUT_data_ready 3000000000 * 4
using android::Condition;
using android::IMemory;
@@ -58,8 +59,10 @@
using android::hardware::Void;
using android::hardware::tv::tuner::V1_0::DemuxDataFormat;
using android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
+using android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
using android::hardware::tv::tuner::V1_0::DemuxFilterPesEvent;
using android::hardware::tv::tuner::V1_0::DemuxFilterSectionEvent;
+using android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings;
using android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
using android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
using android::hardware::tv::tuner::V1_0::DemuxFilterType;
@@ -130,9 +133,6 @@
const uint16_t FMQ_SIZE_4K = 0x1000;
const uint32_t FMQ_SIZE_1M = 0x100000;
-// Equal to SECTION_WRITE_COUNT on the HAL impl side
-// The HAL impl will repeatedly write to the FMQ the count times
-const uint16_t SECTION_READ_COUNT = 10;
struct FilterConf {
DemuxFilterType type;
@@ -214,11 +214,15 @@
class DemuxCallback : public IDemuxCallback {
public:
virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent) override {
- ALOGW("[VTS] FILTER EVENT %d", filterEvent.filterId);
android::Mutex::Autolock autoLock(mMsgLock);
- mFilterEventReceived = true;
+ // 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
mFilterIdToEvent[filterEvent.filterId] = filterEvent;
- startFilterEventThread(filterEvent);
+ readFilterEventData(filterEvent.filterId);
+ mPidFilterOutputCount++;
+ // mFilterIdToMQ.erase(filterEvent.filterId);
+
+ // startFilterEventThread(filterEvent);
mMsgCondition.signal();
return Void();
}
@@ -232,13 +236,16 @@
virtual Return<void> onInputStatus(DemuxInputStatus status) override {
// android::Mutex::Autolock autoLock(mMsgLock);
+ ALOGW("[vts] input status %d", status);
switch (status) {
case DemuxInputStatus::SPACE_EMPTY:
case DemuxInputStatus::SPACE_ALMOST_EMPTY:
+ ALOGW("[vts] keep inputing %d", status);
mKeepWritingInputFMQ = true;
break;
case DemuxInputStatus::SPACE_ALMOST_FULL:
case DemuxInputStatus::SPACE_FULL:
+ ALOGW("[vts] stop inputing %d", status);
mKeepWritingInputFMQ = false;
break;
}
@@ -246,78 +253,64 @@
}
void testOnFilterEvent(uint32_t filterId);
- void testOnSectionFilterEvent(sp<IDemux>& demux, uint32_t filterId, MQDesc& filterMQDescriptor,
- MQDesc& inputMQDescriptor);
void testFilterDataOutput();
- // Legacy
- bool readAndCompareSectionEventData(uint32_t filterId);
+ void stopInputThread();
void startPlaybackInputThread(InputConf inputConf, MQDesc& inputMQDescriptor);
void startFilterEventThread(DemuxFilterEvent event);
static void* __threadLoopInput(void* threadArgs);
static void* __threadLoopFilter(void* threadArgs);
- void inputThreadLoop(InputConf inputConf, bool* keepWritingInputFMQ, MQDesc& inputMQDescriptor);
+ void inputThreadLoop(InputConf* inputConf, bool* keepWritingInputFMQ);
void filterThreadLoop(DemuxFilterEvent& event);
void updateFilterMQ(uint32_t filterId, MQDesc& filterMQDescriptor);
void updateGoldenOutputMap(uint32_t filterId, string goldenOutputFile);
+ bool readFilterEventData(uint32_t filterId);
private:
struct InputThreadArgs {
DemuxCallback* user;
- InputConf inputConf;
+ InputConf* inputConf;
bool* keepWritingInputFMQ;
- MQDesc& inputMQDesc;
};
struct FilterThreadArgs {
DemuxCallback* user;
- DemuxFilterEvent& event;
+ DemuxFilterEvent event;
};
uint16_t mDataLength = 0;
std::vector<uint8_t> mDataOutputBuffer;
bool mFilterEventReceived;
std::map<uint32_t, string> mFilterIdToGoldenOutput;
- std::map<uint32_t, DemuxFilterEvent> mFilterIdToEvent;
std::map<uint32_t, std::unique_ptr<FilterMQ>> mFilterIdToMQ;
std::unique_ptr<FilterMQ> mInputMQ;
std::map<uint32_t, EventFlag*> mFilterIdToMQEventFlag;
+ std::map<uint32_t, DemuxFilterEvent> mFilterIdToEvent;
EventFlag* mInputMQEventFlag;
android::Mutex mMsgLock;
android::Mutex mFilterOutputLock;
+ android::Mutex mInputThreadLock;
android::Condition mMsgCondition;
android::Condition mFilterOutputCondition;
- bool mKeepWritingInputFMQ;
+ bool mKeepWritingInputFMQ = true;
bool mInputThreadRunning;
pthread_t mInputThread;
pthread_t mFilterThread;
+
+ int mPidFilterOutputCount = 0;
};
-// Legacy
-void DemuxCallback::testOnFilterEvent(uint32_t filterId) {
- android::Mutex::Autolock autoLock(mMsgLock);
- while (!mFilterEventReceived) {
- if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
- EXPECT_TRUE(false) << "filter event not received within timeout";
- return;
- }
- }
- // Reset the filter event recieved flag
- mFilterEventReceived = false;
- // Check if filter id match
- EXPECT_TRUE(filterId == mFilterIdToEvent[filterId].filterId) << "filter id match";
-}
-
void DemuxCallback::startPlaybackInputThread(InputConf inputConf, MQDesc& inputMQDescriptor) {
+ mInputMQ = std::make_unique<FilterMQ>(inputMQDescriptor, true /* resetPointers */);
+ EXPECT_TRUE(mInputMQ);
struct InputThreadArgs* threadArgs =
(struct InputThreadArgs*)malloc(sizeof(struct InputThreadArgs));
threadArgs->user = this;
- threadArgs->inputConf = inputConf;
+ threadArgs->inputConf = &inputConf;
threadArgs->keepWritingInputFMQ = &mKeepWritingInputFMQ;
- threadArgs->inputMQDesc = inputMQDescriptor;
pthread_create(&mInputThread, NULL, __threadLoopInput, (void*)threadArgs);
pthread_setname_np(mInputThread, "test_playback_input_loop");
@@ -334,72 +327,22 @@
}
void DemuxCallback::testFilterDataOutput() {
- android::Mutex::Autolock autoLock(mFilterOutputLock);
- while (!mFilterIdToMQ.empty()) {
- if (-ETIMEDOUT == mFilterOutputCondition.waitRelative(mFilterOutputLock, WAIT_TIMEOUT)) {
- EXPECT_TRUE(false) << "filter output does not match golden output within timeout";
+ 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");
}
-// Legacy
-void DemuxCallback::testOnSectionFilterEvent(sp<IDemux>& demux, uint32_t filterId,
- MQDesc& filterMQDescriptor,
- MQDesc& inputMQDescriptor) {
- Result status;
- // Create MQ to read the output into the local buffer
- mFilterIdToMQ[filterId] =
- std::make_unique<FilterMQ>(filterMQDescriptor, true /* resetPointers */);
- EXPECT_TRUE(mFilterIdToMQ[filterId]);
- // Get the MQ to write the input to the HAL
- mInputMQ = std::make_unique<FilterMQ>(inputMQDescriptor, true /* resetPointers */);
- EXPECT_TRUE(mInputMQ);
- // Create the EventFlag that is used to signal the HAL impl that data have been
- // read the Filter FMQ
- EXPECT_TRUE(EventFlag::createEventFlag(mFilterIdToMQ[filterId]->getEventFlagWord(),
- &mFilterIdToMQEventFlag[filterId]) == android::OK);
- // Create the EventFlag that is used to signal the HAL impl that data have been
- // written into the Input FMQ
- EXPECT_TRUE(EventFlag::createEventFlag(mInputMQ->getEventFlagWord(), &mInputMQEventFlag) ==
- android::OK);
- // Start filter
- status = demux->startFilter(filterId);
- status = demux->startInput();
+void DemuxCallback::stopInputThread() {
+ mInputThreadRunning = false;
+ mKeepWritingInputFMQ = false;
- EXPECT_EQ(status, Result::SUCCESS);
- // Test start filter and receive callback event
- for (int i = 0; i < SECTION_READ_COUNT; i++) {
- // Write input FMQ and notify the Tuner Implementation
- EXPECT_TRUE(mInputMQ->write(goldenDataOutputBuffer.data(), goldenDataOutputBuffer.size()));
- mInputMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
- testOnFilterEvent(filterId);
- // checksum of mDataOutputBuffer and Input golden input
- if (readAndCompareSectionEventData(filterId) && i < SECTION_READ_COUNT - 1) {
- mFilterIdToMQEventFlag[filterId]->wake(
- static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
- }
- }
-}
-
-// Legacy
-bool DemuxCallback::readAndCompareSectionEventData(uint32_t filterId) {
- bool result = false;
- DemuxFilterEvent filterEvent = mFilterIdToEvent[filterId];
- for (int i = 0; i < filterEvent.events.size(); i++) {
- DemuxFilterSectionEvent event = filterEvent.events[i].section();
- mDataLength = event.dataLength;
- EXPECT_TRUE(mDataLength == goldenDataOutputBuffer.size()) << "buffer size does not match";
-
- mDataOutputBuffer.resize(mDataLength);
- result = mFilterIdToMQ[filterId]->read(mDataOutputBuffer.data(), mDataLength);
- EXPECT_TRUE(result) << "can't read from Filter MQ";
-
- for (int i = 0; i < mDataLength; i++) {
- EXPECT_TRUE(goldenDataOutputBuffer[i] == mDataOutputBuffer[i]) << "data does not match";
- }
- }
- return result;
+ android::Mutex::Autolock autoLock(mInputThreadLock);
}
void DemuxCallback::updateFilterMQ(uint32_t filterId, MQDesc& filterMQDescriptor) {
@@ -418,58 +361,60 @@
DemuxCallback* const self =
static_cast<DemuxCallback*>(((struct InputThreadArgs*)threadArgs)->user);
self->inputThreadLoop(((struct InputThreadArgs*)threadArgs)->inputConf,
- ((struct InputThreadArgs*)threadArgs)->keepWritingInputFMQ,
- ((struct InputThreadArgs*)threadArgs)->inputMQDesc);
+ ((struct InputThreadArgs*)threadArgs)->keepWritingInputFMQ);
return 0;
}
-void DemuxCallback::inputThreadLoop(InputConf inputConf, bool* keepWritingInputFMQ,
- MQDesc& inputMQDescriptor) {
+void DemuxCallback::inputThreadLoop(InputConf* inputConf, bool* keepWritingInputFMQ) {
+ android::Mutex::Autolock autoLock(mInputThreadLock);
mInputThreadRunning = true;
- std::unique_ptr inputMQ =
- std::make_unique<FilterMQ>(inputMQDescriptor, true /* resetPointers */);
- EXPECT_TRUE(inputMQ);
-
// Create the EventFlag that is used to signal the HAL impl that data have been
// written into the Input FMQ
EventFlag* inputMQEventFlag;
- EXPECT_TRUE(EventFlag::createEventFlag(inputMQ->getEventFlagWord(), &inputMQEventFlag) ==
+ EXPECT_TRUE(EventFlag::createEventFlag(mInputMQ->getEventFlagWord(), &inputMQEventFlag) ==
android::OK);
// open the stream and get its length
- std::ifstream inputData(inputConf.inputDataFile /*"ts/test1.ts"*/, std::ifstream::binary);
- int writeSize = inputConf.setting.packetSize * 6;
+ std::ifstream inputData(inputConf->inputDataFile, std::ifstream::binary);
+ int writeSize = inputConf->setting.packetSize * 6;
char* buffer = new char[writeSize];
- if (!inputData) {
- // log
+ ALOGW("[vts] input thread loop start %s", inputConf->inputDataFile.c_str());
+ if (!inputData.is_open()) {
mInputThreadRunning = false;
+ ALOGW("[vts] Error %s", strerror(errno));
}
while (mInputThreadRunning) {
- // move the stream pointer for packet size * 2k? every read until end
+ // move the stream pointer for packet size * 6 every read until the end
while (*keepWritingInputFMQ) {
inputData.read(buffer, writeSize);
if (!inputData) {
int leftSize = inputData.gcount();
+ if (leftSize == 0) {
+ mInputThreadRunning = false;
+ break;
+ }
inputData.clear();
inputData.read(buffer, leftSize);
// Write the left over of the input data and quit the thread
if (leftSize > 0) {
- EXPECT_TRUE(inputMQ->write((unsigned char*)&buffer[0],
- leftSize / inputConf.setting.packetSize));
+ EXPECT_TRUE(mInputMQ->write((unsigned char*)&buffer[0], leftSize));
inputMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
}
mInputThreadRunning = false;
break;
}
// Write input FMQ and notify the Tuner Implementation
- EXPECT_TRUE(inputMQ->write((unsigned char*)&buffer[0], 6));
+ EXPECT_TRUE(mInputMQ->write((unsigned char*)&buffer[0], writeSize));
inputMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
inputData.seekg(writeSize, inputData.cur);
+ sleep(1);
}
}
+ ALOGW("[vts] Input thread end.");
+
delete[] buffer;
inputData.close();
}
@@ -481,9 +426,9 @@
return 0;
}
-void DemuxCallback::filterThreadLoop(DemuxFilterEvent& /*event*/) {
+void DemuxCallback::filterThreadLoop(DemuxFilterEvent& /* event */) {
android::Mutex::Autolock autoLock(mFilterOutputLock);
- // Read from MQ[event.filterId] per event and filter type
+ // Read from mFilterIdToMQ[event.filterId] per event and filter type
// Assemble to filterOutput[filterId]
@@ -494,6 +439,30 @@
// end thread
}
+bool DemuxCallback::readFilterEventData(uint32_t filterId) {
+ bool result = false;
+ DemuxFilterEvent filterEvent = mFilterIdToEvent[filterId];
+ ALOGW("[vts] reading from filter FMQ %d", filterId);
+ // todo separate filter handlers
+ for (int i = 0; i < filterEvent.events.size(); i++) {
+ DemuxFilterPesEvent event = filterEvent.events[i].pes();
+ mDataLength = event.dataLength;
+ // EXPECT_TRUE(mDataLength == goldenDataOutputBuffer.size()) << "buffer size does not
+ // match";
+
+ mDataOutputBuffer.resize(mDataLength);
+ result = mFilterIdToMQ[filterId]->read(mDataOutputBuffer.data(), mDataLength);
+ EXPECT_TRUE(result) << "can't read from Filter MQ";
+
+ /*for (int i = 0; i < mDataLength; i++) {
+ EXPECT_TRUE(goldenDataOutputBuffer[i] == mDataOutputBuffer[i]) << "data does not match";
+ }*/
+ }
+ mFilterIdToMQEventFlag[filterId]->wake(
+ static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
+ return result;
+}
+
// Test environment for Tuner HIDL HAL.
class TunerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
public:
@@ -528,6 +497,7 @@
sp<DemuxCallback> mDemuxCallback;
MQDesc mFilterMQDescriptor;
MQDesc mInputMQDescriptor;
+ vector<uint32_t> mUsedFilterIds;
uint32_t mDemuxId;
uint32_t mFilterId;
@@ -540,7 +510,8 @@
::testing::AssertionResult stopTuneFrontend(int32_t frontendId);
::testing::AssertionResult closeFrontend(int32_t frontendId);
::testing::AssertionResult createDemux();
- ::testing::AssertionResult createDemuxWithFrontend(int32_t frontendId);
+ ::testing::AssertionResult createDemuxWithFrontend(int32_t frontendId,
+ FrontendSettings settings);
::testing::AssertionResult getInputMQDescriptor();
::testing::AssertionResult addInputToDemux(DemuxInputSettings setting);
::testing::AssertionResult addFilterToDemux(DemuxFilterType type, DemuxFilterSettings setting);
@@ -552,10 +523,8 @@
::testing::AssertionResult playbackDataFlowTest(vector<FilterConf> filterConf,
InputConf inputConf,
vector<string> goldenOutputFiles);
-
- // Legacy
- ::testing::AssertionResult addSectionFilterToDemux();
- ::testing::AssertionResult readSectionFilterDataOutput();
+ ::testing::AssertionResult broadcastDataFlowTest(vector<FilterConf> filterConf,
+ vector<string> goldenOutputFiles);
};
::testing::AssertionResult TunerHidlTest::createFrontend(int32_t frontendId) {
@@ -586,7 +555,7 @@
.frequency = 0,
.modulation = FrontendAtscModulation::UNDEFINED,
};
- frontendSettings.atsc() = frontendAtscSettings;
+ frontendSettings.atsc(frontendAtscSettings);
mFrontendCallback->testOnEvent(mFrontend, frontendSettings);
FrontendDvbtSettings frontendDvbtSettings{
@@ -630,7 +599,8 @@
return ::testing::AssertionResult(status == Result::SUCCESS);
}
-::testing::AssertionResult TunerHidlTest::createDemuxWithFrontend(int32_t frontendId) {
+::testing::AssertionResult TunerHidlTest::createDemuxWithFrontend(int32_t frontendId,
+ FrontendSettings settings) {
Result status;
if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
@@ -641,6 +611,8 @@
return ::testing::AssertionFailure();
}
+ mFrontendCallback->testOnEvent(mFrontend, settings);
+
status = mDemux->setFrontendDataSource(frontendId);
return ::testing::AssertionResult(status == Result::SUCCESS);
@@ -705,7 +677,7 @@
mDemuxCallback = new DemuxCallback();
}
- // Add section filter to the local demux
+ // Add playback input to the local demux
status = mDemux->addInput(FMQ_SIZE_1M, mDemuxCallback);
if (status != Result::SUCCESS) {
@@ -732,29 +704,6 @@
return ::testing::AssertionResult(status == Result::SUCCESS);
}
-// Legacy
-::testing::AssertionResult TunerHidlTest::addSectionFilterToDemux() {
- Result status;
-
- if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
- return ::testing::AssertionFailure();
- }
-
- // Create demux callback
- if (!mDemuxCallback) {
- mDemuxCallback = new DemuxCallback();
- }
-
- // Add section filter to the local demux
- mDemux->addFilter(DemuxFilterType::SECTION, FMQ_SIZE_4K, mDemuxCallback,
- [&](Result result, uint32_t filterId) {
- mFilterId = filterId;
- status = result;
- });
-
- return ::testing::AssertionResult(status == Result::SUCCESS);
-}
-
::testing::AssertionResult TunerHidlTest::addFilterToDemux(DemuxFilterType type,
DemuxFilterSettings setting) {
Result status;
@@ -800,36 +749,10 @@
return ::testing::AssertionResult(status == Result::SUCCESS);
}
-// Legacy
-::testing::AssertionResult TunerHidlTest::readSectionFilterDataOutput() {
- // Filter Configuration Module
- DemuxInputSettings setting{
- .statusMask = 0xf,
- .lowThreshold = 0x1000,
- .highThreshold = 0x100000,
- .dataFormat = DemuxDataFormat::TS,
- .packetSize = 188,
- };
- if (addSectionFilterToDemux() == ::testing::AssertionFailure() ||
- getFilterMQDescriptor(mFilterId) == ::testing::AssertionFailure() ||
- addInputToDemux(setting) == ::testing::AssertionFailure() ||
- getInputMQDescriptor() == ::testing::AssertionFailure()) {
- return ::testing::AssertionFailure();
- }
-
- // Data Verify Module
- // Test start filter and read the output data
- mDemuxCallback->testOnSectionFilterEvent(mDemux, mFilterId, mFilterMQDescriptor,
- mInputMQDescriptor);
-
- // Clean Up Module
- return closeDemux(); //::testing::AssertionSuccess();
-}
-
-::testing::AssertionResult TunerHidlTest::playbackDataFlowTest(vector<FilterConf> filterConf,
- InputConf inputConf,
- vector<string> goldenOutputFiles) {
+::testing::AssertionResult TunerHidlTest::playbackDataFlowTest(
+ vector<FilterConf> filterConf, InputConf inputConf, vector<string> /*goldenOutputFiles*/) {
Result status;
+ int filterIdsSize;
// Filter Configuration Module
for (int i = 0; i < filterConf.size(); i++) {
if (addFilterToDemux(filterConf[i].type, filterConf[i].setting) ==
@@ -838,8 +761,11 @@
getFilterMQDescriptor(mFilterId) == ::testing::AssertionFailure()) {
return ::testing::AssertionFailure();
}
+ filterIdsSize = mUsedFilterIds.size();
+ mUsedFilterIds.resize(filterIdsSize + 1);
+ mUsedFilterIds[filterIdsSize] = mFilterId;
mDemuxCallback->updateFilterMQ(mFilterId, mFilterMQDescriptor);
- mDemuxCallback->updateGoldenOutputMap(mFilterId, goldenOutputFiles[i]);
+ // mDemuxCallback->updateGoldenOutputMap(mFilterId, goldenOutputFiles[i]);
status = mDemux->startFilter(mFilterId);
if (status != Result::SUCCESS) {
return ::testing::AssertionFailure();
@@ -860,8 +786,76 @@
// Data Verify Module
mDemuxCallback->testFilterDataOutput();
+ mDemuxCallback->stopInputThread();
// Clean Up Module
+ for (int i = 0; i <= filterIdsSize; i++) {
+ if (mDemux->stopFilter(mUsedFilterIds[i]) != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+ }
+ if (mDemux->stopInput() != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+ return closeDemux();
+}
+
+::testing::AssertionResult TunerHidlTest::broadcastDataFlowTest(
+ vector<FilterConf> filterConf, vector<string> /*goldenOutputFiles*/) {
+ Result status;
+ hidl_vec<FrontendId> feIds;
+
+ mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
+ status = result;
+ feIds = frontendIds;
+ });
+
+ if (feIds.size() == 0) {
+ ALOGW("[ WARN ] Frontend isn't available");
+ return ::testing::AssertionFailure();
+ }
+
+ FrontendDvbtSettings dvbt{
+ .frequency = 1000,
+ };
+ FrontendSettings settings;
+ settings.dvbt(dvbt);
+
+ if (createDemuxWithFrontend(feIds[0], settings) != ::testing::AssertionSuccess()) {
+ return ::testing::AssertionFailure();
+ }
+
+ int filterIdsSize;
+ // Filter Configuration Module
+ for (int i = 0; i < filterConf.size(); i++) {
+ if (addFilterToDemux(filterConf[i].type, filterConf[i].setting) ==
+ ::testing::AssertionFailure() ||
+ // TODO use a map to save the FMQs/EvenFlags and pass to callback
+ getFilterMQDescriptor(mFilterId) == ::testing::AssertionFailure()) {
+ return ::testing::AssertionFailure();
+ }
+ filterIdsSize = mUsedFilterIds.size();
+ mUsedFilterIds.resize(filterIdsSize + 1);
+ mUsedFilterIds[filterIdsSize] = mFilterId;
+ mDemuxCallback->updateFilterMQ(mFilterId, mFilterMQDescriptor);
+ status = mDemux->startFilter(mFilterId);
+ if (status != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+ }
+
+ // Data Verify Module
+ mDemuxCallback->testFilterDataOutput();
+
+ // Clean Up Module
+ for (int i = 0; i <= filterIdsSize; i++) {
+ if (mDemux->stopFilter(mUsedFilterIds[i]) != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+ }
+ if (mFrontend->stopTune() != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
return closeDemux();
}
@@ -948,7 +942,7 @@
}
}
-TEST_F(TunerHidlTest, CreateDemuxWithFrontend) {
+/*TEST_F(TunerHidlTest, CreateDemuxWithFrontend) {
Result status;
hidl_vec<FrontendId> feIds;
@@ -963,10 +957,17 @@
return;
}
+ FrontendDvbtSettings dvbt{
+ .frequency = 1000,
+ };
+ FrontendSettings settings;
+ settings.dvbt(dvbt);
+
for (size_t i = 0; i < feIds.size(); i++) {
- ASSERT_TRUE(createDemuxWithFrontend(feIds[i]));
+ ASSERT_TRUE(createDemuxWithFrontend(feIds[i], settings));
+ mFrontend->stopTune();
}
-}
+}*/
TEST_F(TunerHidlTest, CreateDemux) {
description("Create Demux");
@@ -991,9 +992,63 @@
/*
* DATA FLOW TESTS
*/
-TEST_F(TunerHidlTest, ReadSectionFilterOutput) {
- description("Read data output from FMQ of a Section Filter");
- ASSERT_TRUE(readSectionFilterDataOutput());
+TEST_F(TunerHidlTest, PlaybackDataFlowWithPesFilterTest) {
+ description("Feed ts data from playback and configure pes filter to get output");
+
+ // todo modulize the filter conf parser
+ vector<FilterConf> filterConf;
+ filterConf.resize(1);
+
+ DemuxFilterSettings filterSetting;
+ DemuxFilterPesDataSettings pesFilterSetting{
+ .tpid = 18,
+ };
+ filterSetting.pesData(pesFilterSetting);
+ FilterConf pesFilterConf{
+ .type = DemuxFilterType::PES,
+ .setting = filterSetting,
+ };
+ filterConf[0] = pesFilterConf;
+
+ DemuxInputSettings inputSetting{
+ .statusMask = 0xf,
+ .lowThreshold = 0x1000,
+ .highThreshold = 0x07fff,
+ .dataFormat = DemuxDataFormat::TS,
+ .packetSize = 188,
+ };
+
+ InputConf inputConf{
+ .inputDataFile = "/vendor/etc/test1.ts",
+ .setting = inputSetting,
+ };
+
+ vector<string> goldenOutputFiles;
+
+ ASSERT_TRUE(playbackDataFlowTest(filterConf, inputConf, goldenOutputFiles));
+}
+
+TEST_F(TunerHidlTest, BroadcastDataFlowWithPesFilterTest) {
+ description("Feed ts data from frontend and test with PES filter");
+
+ // todo modulize the filter conf parser
+ vector<FilterConf> filterConf;
+ filterConf.resize(1);
+
+ DemuxFilterSettings filterSetting;
+ DemuxFilterPesDataSettings pesFilterSetting{
+ .tpid = 18,
+ };
+ filterSetting.pesData(pesFilterSetting);
+ FilterConf pesFilterConf{
+ .type = DemuxFilterType::PES,
+ .setting = filterSetting,
+ };
+ filterConf[0] = pesFilterConf;
+
+ vector<string> goldenOutputFiles;
+
+ ASSERT_TRUE(broadcastDataFlowTest(filterConf, goldenOutputFiles));
}
} // namespace
diff --git a/tv/tuner/README.md b/tv/tuner/README.md
new file mode 100644
index 0000000..a833c87
--- /dev/null
+++ b/tv/tuner/README.md
@@ -0,0 +1,12 @@
+# Tuner HALs
+
+## Overview
+
+TV specific tuners.
+
+Sett 1.0/ITuner.hal for an overview.
+
+*** note
+**Warning:** The HALs are not (yet) frozen, as the HAL definition is
+expected to evolve between Android releases.
+***