Merge changes from topic "record" am: e843b7c118
am: 360776a89e
Change-Id: Idfc4ab4cc53b2f63fb353c3d68952262d3719b72
This commit is contained in:
commit
4b73b59554
10 changed files with 531 additions and 56 deletions
|
@ -104,11 +104,11 @@ interface IFilter {
|
|||
*
|
||||
* It is used by the client to ask the hardware resource id for the filter.
|
||||
*
|
||||
* @param filterId the hardware resource Id for the filter.
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
* @return filterId the hardware resource Id for the filter.
|
||||
*/
|
||||
getId() generates (Result result, uint32_t filterId);
|
||||
|
||||
|
|
|
@ -51,7 +51,8 @@ Return<Result> Demux::setFrontendDataSource(uint32_t frontendId) {
|
|||
mFrontendSourceFile = mFrontend->getSourceFile();
|
||||
|
||||
mTunerService->setFrontendAsDemuxSource(frontendId, mDemuxId);
|
||||
return startBroadcastInputLoop();
|
||||
|
||||
return startFrontendInputLoop();
|
||||
}
|
||||
|
||||
Return<void> Demux::openFilter(const DemuxFilterType& type, uint32_t bufferSize,
|
||||
|
@ -136,14 +137,14 @@ Return<void> Demux::openDvr(DvrType type, uint32_t bufferSize, const sp<IDvrCall
|
|||
return Void();
|
||||
}
|
||||
|
||||
sp<Dvr> dvr = new Dvr(type, bufferSize, cb, this);
|
||||
mDvr = new Dvr(type, bufferSize, cb, this);
|
||||
|
||||
if (!dvr->createDvrMQ()) {
|
||||
_hidl_cb(Result::UNKNOWN_ERROR, dvr);
|
||||
if (!mDvr->createDvrMQ()) {
|
||||
_hidl_cb(Result::UNKNOWN_ERROR, mDvr);
|
||||
return Void();
|
||||
}
|
||||
|
||||
_hidl_cb(Result::SUCCESS, dvr);
|
||||
_hidl_cb(Result::SUCCESS, mDvr);
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
@ -166,13 +167,14 @@ Result Demux::removeFilter(uint32_t filterId) {
|
|||
|
||||
// resetFilterRecords(filterId);
|
||||
mUsedFilterIds.erase(filterId);
|
||||
mRecordFilterIds.erase(filterId);
|
||||
mUnusedFilterIds.insert(filterId);
|
||||
mFilters.erase(filterId);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
void Demux::startTsFilter(vector<uint8_t> data) {
|
||||
void Demux::startBroadcastTsFilter(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));
|
||||
|
@ -185,7 +187,17 @@ void Demux::startTsFilter(vector<uint8_t> data) {
|
|||
}
|
||||
}
|
||||
|
||||
bool Demux::startFilterDispatcher() {
|
||||
void Demux::sendFrontendInputToRecord(vector<uint8_t> data) {
|
||||
set<uint32_t>::iterator it;
|
||||
for (it = mRecordFilterIds.begin(); it != mRecordFilterIds.end(); it++) {
|
||||
if (DEBUG_FILTER) {
|
||||
ALOGW("update record filter output");
|
||||
}
|
||||
mFilters[*it]->updateRecordOutput(data);
|
||||
}
|
||||
}
|
||||
|
||||
bool Demux::startBroadcastFilterDispatcher() {
|
||||
set<uint32_t>::iterator it;
|
||||
|
||||
// Handle the output data per filter type
|
||||
|
@ -198,6 +210,18 @@ bool Demux::startFilterDispatcher() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Demux::startRecordFilterDispatcher() {
|
||||
set<uint32_t>::iterator it;
|
||||
|
||||
for (it = mRecordFilterIds.begin(); it != mRecordFilterIds.end(); it++) {
|
||||
if (mFilters[*it]->startRecordFilterHandler() != Result::SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Result Demux::startFilterHandler(uint32_t filterId) {
|
||||
return mFilters[filterId]->startFilterHandler();
|
||||
}
|
||||
|
@ -210,22 +234,22 @@ uint16_t Demux::getFilterTpid(uint32_t filterId) {
|
|||
return mFilters[filterId]->getTpid();
|
||||
}
|
||||
|
||||
Result Demux::startBroadcastInputLoop() {
|
||||
pthread_create(&mBroadcastInputThread, NULL, __threadLoopBroadcast, this);
|
||||
pthread_setname_np(mBroadcastInputThread, "broadcast_input_thread");
|
||||
Result Demux::startFrontendInputLoop() {
|
||||
pthread_create(&mFrontendInputThread, NULL, __threadLoopFrontend, this);
|
||||
pthread_setname_np(mFrontendInputThread, "frontend_input_thread");
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
void* Demux::__threadLoopBroadcast(void* user) {
|
||||
void* Demux::__threadLoopFrontend(void* user) {
|
||||
Demux* const self = static_cast<Demux*>(user);
|
||||
self->broadcastInputThreadLoop();
|
||||
self->frontendInputThreadLoop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Demux::broadcastInputThreadLoop() {
|
||||
std::lock_guard<std::mutex> lock(mBroadcastInputThreadLock);
|
||||
mBroadcastInputThreadRunning = true;
|
||||
void Demux::frontendInputThreadLoop() {
|
||||
std::lock_guard<std::mutex> lock(mFrontendInputThreadLock);
|
||||
mFrontendInputThreadRunning = true;
|
||||
mKeepFetchingDataFromFrontend = true;
|
||||
|
||||
// open the stream and get its length
|
||||
|
@ -234,20 +258,20 @@ void Demux::broadcastInputThreadLoop() {
|
|||
int packetSize = 188;
|
||||
int writePacketAmount = 6;
|
||||
char* buffer = new char[packetSize];
|
||||
ALOGW("[Demux] broadcast input thread loop start %s", mFrontendSourceFile.c_str());
|
||||
ALOGW("[Demux] Frontend input thread loop start %s", mFrontendSourceFile.c_str());
|
||||
if (!inputData.is_open()) {
|
||||
mBroadcastInputThreadRunning = false;
|
||||
mFrontendInputThreadRunning = false;
|
||||
ALOGW("[Demux] Error %s", strerror(errno));
|
||||
}
|
||||
|
||||
while (mBroadcastInputThreadRunning) {
|
||||
while (mFrontendInputThreadRunning) {
|
||||
// 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) {
|
||||
mKeepFetchingDataFromFrontend = false;
|
||||
mBroadcastInputThreadRunning = false;
|
||||
mFrontendInputThreadRunning = false;
|
||||
break;
|
||||
}
|
||||
// filter and dispatch filter output
|
||||
|
@ -256,23 +280,61 @@ void Demux::broadcastInputThreadLoop() {
|
|||
for (int index = 0; index < byteBuffer.size(); index++) {
|
||||
byteBuffer[index] = static_cast<uint8_t>(buffer[index]);
|
||||
}
|
||||
startTsFilter(byteBuffer);
|
||||
if (mIsRecording) {
|
||||
// Feed the data into the Dvr recording input
|
||||
sendFrontendInputToRecord(byteBuffer);
|
||||
} else {
|
||||
// Feed the data into the broadcast demux filter
|
||||
startBroadcastTsFilter(byteBuffer);
|
||||
}
|
||||
}
|
||||
if (mIsRecording) {
|
||||
// Dispatch the data into the broadcasting filters.
|
||||
startRecordFilterDispatcher();
|
||||
} else {
|
||||
// Dispatch the data into the broadcasting filters.
|
||||
startBroadcastFilterDispatcher();
|
||||
}
|
||||
startFilterDispatcher();
|
||||
usleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
ALOGW("[Demux] Broadcast Input thread end.");
|
||||
ALOGW("[Demux] Frontend Input thread end.");
|
||||
delete[] buffer;
|
||||
inputData.close();
|
||||
}
|
||||
|
||||
void Demux::stopBroadcastInput() {
|
||||
void Demux::stopFrontendInput() {
|
||||
ALOGD("[Demux] stop frontend on demux");
|
||||
mKeepFetchingDataFromFrontend = false;
|
||||
mBroadcastInputThreadRunning = false;
|
||||
std::lock_guard<std::mutex> lock(mBroadcastInputThreadLock);
|
||||
mFrontendInputThreadRunning = false;
|
||||
std::lock_guard<std::mutex> lock(mFrontendInputThreadLock);
|
||||
}
|
||||
|
||||
void Demux::setIsRecording(bool isRecording) {
|
||||
mIsRecording = isRecording;
|
||||
}
|
||||
|
||||
bool Demux::attachRecordFilter(int filterId) {
|
||||
if (mFilters[filterId] == nullptr || mDvr == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mRecordFilterIds.insert(filterId);
|
||||
mFilters[filterId]->attachFilterToRecord(mDvr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Demux::detachRecordFilter(int filterId) {
|
||||
if (mFilters[filterId] == nullptr || mDvr == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mRecordFilterIds.erase(filterId);
|
||||
mFilters[filterId]->detachFilterFromRecord();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
|
|
|
@ -81,11 +81,14 @@ class Demux : public IDemux {
|
|||
virtual Return<Result> disconnectCiCam() override;
|
||||
|
||||
// Functions interacts with Tuner Service
|
||||
void stopBroadcastInput();
|
||||
void stopFrontendInput();
|
||||
Result removeFilter(uint32_t filterId);
|
||||
bool attachRecordFilter(int filterId);
|
||||
bool detachRecordFilter(int filterId);
|
||||
Result startFilterHandler(uint32_t filterId);
|
||||
void updateFilterOutput(uint16_t filterId, vector<uint8_t> data);
|
||||
uint16_t getFilterTpid(uint32_t filterId);
|
||||
void setIsRecording(bool isRecording);
|
||||
|
||||
private:
|
||||
// Tuner service
|
||||
|
@ -101,9 +104,9 @@ class Demux : public IDemux {
|
|||
uint32_t filterId;
|
||||
};
|
||||
|
||||
Result startBroadcastInputLoop();
|
||||
static void* __threadLoopBroadcast(void* user);
|
||||
void broadcastInputThreadLoop();
|
||||
Result startFrontendInputLoop();
|
||||
static void* __threadLoopFrontend(void* user);
|
||||
void frontendInputThreadLoop();
|
||||
|
||||
/**
|
||||
* To create a FilterMQ with the the next available Filter ID.
|
||||
|
@ -117,9 +120,13 @@ class Demux : public IDemux {
|
|||
/**
|
||||
* A dispatcher to read and dispatch input data to all the started filters.
|
||||
* Each filter handler handles the data filtering/output writing/filterEvent updating.
|
||||
* Note that recording filters are not included.
|
||||
*/
|
||||
bool startFilterDispatcher();
|
||||
void startTsFilter(vector<uint8_t> data);
|
||||
bool startBroadcastFilterDispatcher();
|
||||
void startBroadcastTsFilter(vector<uint8_t> data);
|
||||
|
||||
void sendFrontendInputToRecord(vector<uint8_t> data);
|
||||
bool startRecordFilterDispatcher();
|
||||
|
||||
uint32_t mDemuxId;
|
||||
uint32_t mCiCamId;
|
||||
|
@ -140,19 +147,33 @@ class Demux : public IDemux {
|
|||
* and added into usedFilterIds.
|
||||
*/
|
||||
set<uint32_t> mUnusedFilterIds;
|
||||
/**
|
||||
* Record all the attached record filter Ids.
|
||||
* Any removed filter id should be removed from this set.
|
||||
*/
|
||||
set<uint32_t> mRecordFilterIds;
|
||||
/**
|
||||
* A list of created FilterMQ ptrs.
|
||||
* The array number is the filter ID.
|
||||
*/
|
||||
std::map<uint32_t, sp<Filter>> mFilters;
|
||||
|
||||
/**
|
||||
* Local reference to the opened DVR object.
|
||||
*/
|
||||
sp<Dvr> mDvr;
|
||||
|
||||
// Thread handlers
|
||||
pthread_t mBroadcastInputThread;
|
||||
pthread_t mFrontendInputThread;
|
||||
/**
|
||||
* If a specific filter's writing loop is still running
|
||||
*/
|
||||
bool mBroadcastInputThreadRunning;
|
||||
bool mFrontendInputThreadRunning;
|
||||
bool mKeepFetchingDataFromFrontend;
|
||||
/**
|
||||
* If the dvr recording is running.
|
||||
*/
|
||||
bool mIsRecording = false;
|
||||
/**
|
||||
* Lock to protect writes to the FMQs
|
||||
*/
|
||||
|
@ -160,7 +181,7 @@ class Demux : public IDemux {
|
|||
/**
|
||||
* Lock to protect writes to the input status
|
||||
*/
|
||||
std::mutex mBroadcastInputThreadLock;
|
||||
std::mutex mFrontendInputThreadLock;
|
||||
|
||||
// temp handle single PES filter
|
||||
// TODO handle mulptiple Pes filters
|
||||
|
|
|
@ -70,7 +70,14 @@ Return<Result> Dvr::attachFilter(const sp<IFilter>& filter) {
|
|||
return status;
|
||||
}
|
||||
|
||||
// check if the attached filter is a record filter
|
||||
|
||||
mFilters[filterId] = filter;
|
||||
mIsRecordFilterAttached = true;
|
||||
if (!mDemux->attachRecordFilter(filterId)) {
|
||||
return Result::INVALID_ARGUMENT;
|
||||
}
|
||||
mDemux->setIsRecording(mIsRecordStarted | mIsRecordFilterAttached);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
@ -95,6 +102,15 @@ Return<Result> Dvr::detachFilter(const sp<IFilter>& filter) {
|
|||
it = mFilters.find(filterId);
|
||||
if (it != mFilters.end()) {
|
||||
mFilters.erase(filterId);
|
||||
if (!mDemux->detachRecordFilter(filterId)) {
|
||||
return Result::INVALID_ARGUMENT;
|
||||
}
|
||||
}
|
||||
|
||||
// If all the filters are detached, record can't be started
|
||||
if (mFilters.empty()) {
|
||||
mIsRecordFilterAttached = false;
|
||||
mDemux->setIsRecording(mIsRecordStarted | mIsRecordFilterAttached);
|
||||
}
|
||||
|
||||
return Result::SUCCESS;
|
||||
|
@ -115,8 +131,9 @@ Return<Result> Dvr::start() {
|
|||
pthread_create(&mDvrThread, NULL, __threadLoopPlayback, this);
|
||||
pthread_setname_np(mDvrThread, "playback_waiting_loop");
|
||||
} else if (mType == DvrType::RECORD) {
|
||||
/*pthread_create(&mInputThread, NULL, __threadLoopInput, this);
|
||||
pthread_setname_np(mInputThread, "playback_waiting_loop");*/
|
||||
mRecordStatus = RecordStatus::DATA_READY;
|
||||
mIsRecordStarted = true;
|
||||
mDemux->setIsRecording(mIsRecordStarted | mIsRecordFilterAttached);
|
||||
}
|
||||
|
||||
// TODO start another thread to send filter status callback to the framework
|
||||
|
@ -131,12 +148,17 @@ Return<Result> Dvr::stop() {
|
|||
|
||||
std::lock_guard<std::mutex> lock(mDvrThreadLock);
|
||||
|
||||
mIsRecordStarted = false;
|
||||
mDemux->setIsRecording(mIsRecordStarted | mIsRecordFilterAttached);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Dvr::flush() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
mRecordStatus = RecordStatus::DATA_READY;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -272,6 +294,45 @@ bool Dvr::startFilterDispatcher() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Dvr::writeRecordFMQ(const std::vector<uint8_t>& data) {
|
||||
std::lock_guard<std::mutex> lock(mWriteLock);
|
||||
ALOGW("[Dvr] write record FMQ");
|
||||
if (mDvrMQ->write(data.data(), data.size())) {
|
||||
mDvrEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
|
||||
maySendRecordStatusCallback();
|
||||
return true;
|
||||
}
|
||||
|
||||
maySendRecordStatusCallback();
|
||||
return false;
|
||||
}
|
||||
|
||||
void Dvr::maySendRecordStatusCallback() {
|
||||
std::lock_guard<std::mutex> lock(mRecordStatusLock);
|
||||
int availableToRead = mDvrMQ->availableToRead();
|
||||
int availableToWrite = mDvrMQ->availableToWrite();
|
||||
|
||||
RecordStatus newStatus = checkRecordStatusChange(availableToWrite, availableToRead,
|
||||
mDvrSettings.record().highThreshold,
|
||||
mDvrSettings.record().lowThreshold);
|
||||
if (mRecordStatus != newStatus) {
|
||||
mCallback->onRecordStatus(newStatus);
|
||||
mRecordStatus = newStatus;
|
||||
}
|
||||
}
|
||||
|
||||
RecordStatus Dvr::checkRecordStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
|
||||
uint32_t highThreshold, uint32_t lowThreshold) {
|
||||
if (availableToWrite == 0) {
|
||||
return DemuxFilterStatus::OVERFLOW;
|
||||
} else if (availableToRead > highThreshold) {
|
||||
return DemuxFilterStatus::HIGH_WATER;
|
||||
} else if (availableToRead < lowThreshold) {
|
||||
return DemuxFilterStatus::LOW_WATER;
|
||||
}
|
||||
return mRecordStatus;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
|
|
|
@ -79,6 +79,8 @@ class Dvr : public IDvr {
|
|||
* Return false is any of the above processes fails.
|
||||
*/
|
||||
bool createDvrMQ();
|
||||
void sendBroadcastInputToDvrRecord(vector<uint8_t> byteBuffer);
|
||||
bool writeRecordFMQ(const std::vector<uint8_t>& data);
|
||||
|
||||
private:
|
||||
// Demux service
|
||||
|
@ -95,6 +97,8 @@ class Dvr : public IDvr {
|
|||
void maySendRecordStatusCallback();
|
||||
PlaybackStatus checkPlaybackStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
|
||||
uint32_t highThreshold, uint32_t lowThreshold);
|
||||
RecordStatus checkRecordStatusChange(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.
|
||||
|
@ -103,9 +107,9 @@ class Dvr : public IDvr {
|
|||
void startTpidFilter(vector<uint8_t> data);
|
||||
bool startFilterDispatcher();
|
||||
static void* __threadLoopPlayback(void* user);
|
||||
static void* __threadLoopBroadcast(void* user);
|
||||
static void* __threadLoopRecord(void* user);
|
||||
void playbackThreadLoop();
|
||||
void broadcastInputThreadLoop();
|
||||
void recordThreadLoop();
|
||||
|
||||
unique_ptr<DvrMQ> mDvrMQ;
|
||||
EventFlag* mDvrEventFlag;
|
||||
|
@ -121,6 +125,7 @@ class Dvr : public IDvr {
|
|||
|
||||
// FMQ status local records
|
||||
PlaybackStatus mPlaybackStatus;
|
||||
RecordStatus mRecordStatus;
|
||||
/**
|
||||
* If a specific filter's writing loop is still running
|
||||
*/
|
||||
|
@ -135,10 +140,16 @@ class Dvr : public IDvr {
|
|||
* Lock to protect writes to the input status
|
||||
*/
|
||||
std::mutex mPlaybackStatusLock;
|
||||
std::mutex mRecordStatusLock;
|
||||
std::mutex mBroadcastInputThreadLock;
|
||||
std::mutex mDvrThreadLock;
|
||||
|
||||
const bool DEBUG_DVR = false;
|
||||
|
||||
// Booleans to check if recording is running.
|
||||
// Recording is ready when both of the following are set to true.
|
||||
bool mIsRecordStarted = false;
|
||||
bool mIsRecordFilterAttached = false;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
|
|
|
@ -265,10 +265,16 @@ uint16_t Filter::getTpid() {
|
|||
|
||||
void Filter::updateFilterOutput(vector<uint8_t> data) {
|
||||
std::lock_guard<std::mutex> lock(mFilterOutputLock);
|
||||
ALOGD("[Filter] handler output updated");
|
||||
ALOGD("[Filter] filter output updated");
|
||||
mFilterOutput.insert(mFilterOutput.end(), data.begin(), data.end());
|
||||
}
|
||||
|
||||
void Filter::updateRecordOutput(vector<uint8_t> data) {
|
||||
std::lock_guard<std::mutex> lock(mRecordFilterOutputLock);
|
||||
ALOGD("[Filter] record filter output updated");
|
||||
mRecordFilterOutput.insert(mRecordFilterOutput.end(), data.begin(), data.end());
|
||||
}
|
||||
|
||||
Result Filter::startFilterHandler() {
|
||||
std::lock_guard<std::mutex> lock(mFilterOutputLock);
|
||||
switch (mType.mainType) {
|
||||
|
@ -292,12 +298,11 @@ Result Filter::startFilterHandler() {
|
|||
case DemuxTsFilterType::PCR:
|
||||
startPcrFilterHandler();
|
||||
break;
|
||||
case DemuxTsFilterType::RECORD:
|
||||
startRecordFilterHandler();
|
||||
break;
|
||||
case DemuxTsFilterType::TEMI:
|
||||
startTemiFilterHandler();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case DemuxFilterMainType::MMTP:
|
||||
|
@ -342,12 +347,16 @@ Result Filter::startPesFilterHandler() {
|
|||
if (mPesSizeLeft == 0) {
|
||||
uint32_t prefix = (mFilterOutput[i + 4] << 16) | (mFilterOutput[i + 5] << 8) |
|
||||
mFilterOutput[i + 6];
|
||||
ALOGD("[Filter] prefix %d", prefix);
|
||||
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;
|
||||
ALOGD("[Filter] pes data length %d", mPesSizeLeft);
|
||||
if (DEBUG_FILTER) {
|
||||
ALOGD("[Filter] pes data length %d", mPesSizeLeft);
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
@ -360,7 +369,9 @@ Result Filter::startPesFilterHandler() {
|
|||
mPesOutput.insert(mPesOutput.end(), first, last);
|
||||
// size does not match then continue
|
||||
mPesSizeLeft -= endPoint;
|
||||
ALOGD("[Filter] pes data left %d", mPesSizeLeft);
|
||||
if (DEBUG_FILTER) {
|
||||
ALOGD("[Filter] pes data left %d", mPesSizeLeft);
|
||||
}
|
||||
if (mPesSizeLeft > 0) {
|
||||
continue;
|
||||
}
|
||||
|
@ -377,7 +388,9 @@ Result Filter::startPesFilterHandler() {
|
|||
.streamId = mPesOutput[3],
|
||||
.dataLength = static_cast<uint16_t>(mPesOutput.size()),
|
||||
};
|
||||
ALOGD("[Filter] assembled pes data length %d", pesEvent.dataLength);
|
||||
if (DEBUG_FILTER) {
|
||||
ALOGD("[Filter] assembled pes data length %d", pesEvent.dataLength);
|
||||
}
|
||||
|
||||
int size = mFilterEvent.events.size();
|
||||
mFilterEvent.events.resize(size + 1);
|
||||
|
@ -413,13 +426,23 @@ Result Filter::startMediaFilterHandler() {
|
|||
}
|
||||
|
||||
Result Filter::startRecordFilterHandler() {
|
||||
DemuxFilterTsRecordEvent tsRecordEvent;
|
||||
/*DemuxFilterTsRecordEvent tsRecordEvent;
|
||||
tsRecordEvent.pid.tPid(0);
|
||||
tsRecordEvent.indexMask.tsIndexMask(0x01);
|
||||
mFilterEvent.events.resize(1);
|
||||
mFilterEvent.events[0].tsRecord(tsRecordEvent);
|
||||
*/
|
||||
std::lock_guard<std::mutex> lock(mRecordFilterOutputLock);
|
||||
if (mRecordFilterOutput.empty()) {
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
mFilterOutput.clear();
|
||||
if (mDvr == nullptr || !mDvr->writeRecordFMQ(mRecordFilterOutput)) {
|
||||
ALOGD("[Filter] dvr fails to write into record FMQ.");
|
||||
return Result::UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mRecordFilterOutput.clear();
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -462,6 +485,14 @@ bool Filter::writeDataToFilterMQ(const std::vector<uint8_t>& data) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Filter::attachFilterToRecord(const sp<Dvr> dvr) {
|
||||
mDvr = dvr;
|
||||
}
|
||||
|
||||
void Filter::detachFilterFromRecord() {
|
||||
mDvr = nullptr;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <math.h>
|
||||
#include <set>
|
||||
#include "Demux.h"
|
||||
#include "Dvr.h"
|
||||
#include "Frontend.h"
|
||||
|
||||
using namespace std;
|
||||
|
@ -44,6 +45,7 @@ using ::android::hardware::tv::tuner::V1_0::Result;
|
|||
using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
|
||||
|
||||
class Demux;
|
||||
class Dvr;
|
||||
|
||||
class Filter : public IFilter {
|
||||
public:
|
||||
|
@ -80,11 +82,17 @@ class Filter : public IFilter {
|
|||
bool createFilterMQ();
|
||||
uint16_t getTpid();
|
||||
void updateFilterOutput(vector<uint8_t> data);
|
||||
void updateRecordOutput(vector<uint8_t> data);
|
||||
Result startFilterHandler();
|
||||
Result startRecordFilterHandler();
|
||||
void attachFilterToRecord(const sp<Dvr> dvr);
|
||||
void detachFilterFromRecord();
|
||||
|
||||
private:
|
||||
// Tuner service
|
||||
sp<Demux> mDemux;
|
||||
// Dvr reference once the filter is attached to any
|
||||
sp<Dvr> mDvr = nullptr;
|
||||
/**
|
||||
* Filter callbacks used on filter events or FMQ status
|
||||
*/
|
||||
|
@ -99,6 +107,7 @@ class Filter : public IFilter {
|
|||
sp<IFilter> mDataSource;
|
||||
bool mIsDataSourceDemux = true;
|
||||
vector<uint8_t> mFilterOutput;
|
||||
vector<uint8_t> mRecordFilterOutput;
|
||||
unique_ptr<FilterMQ> mFilterMQ;
|
||||
EventFlag* mFilterEventFlag;
|
||||
DemuxFilterEvent mFilterEvent;
|
||||
|
@ -120,6 +129,8 @@ class Filter : public IFilter {
|
|||
*/
|
||||
const uint16_t SECTION_WRITE_COUNT = 10;
|
||||
|
||||
bool DEBUG_FILTER = false;
|
||||
|
||||
/**
|
||||
* Filter handlers to handle the data filtering.
|
||||
* They are also responsible to write the filtered output into the filter FMQ
|
||||
|
@ -129,7 +140,6 @@ class Filter : public IFilter {
|
|||
Result startPesFilterHandler();
|
||||
Result startTsFilterHandler();
|
||||
Result startMediaFilterHandler();
|
||||
Result startRecordFilterHandler();
|
||||
Result startPcrFilterHandler();
|
||||
Result startTemiFilterHandler();
|
||||
Result startFilterLoop();
|
||||
|
@ -165,6 +175,7 @@ class Filter : public IFilter {
|
|||
std::mutex mFilterStatusLock;
|
||||
std::mutex mFilterThreadLock;
|
||||
std::mutex mFilterOutputLock;
|
||||
std::mutex mRecordFilterOutputLock;
|
||||
|
||||
// temp handle single PES filter
|
||||
// TODO handle mulptiple Pes filters
|
||||
|
|
|
@ -148,7 +148,7 @@ void Tuner::frontendStopTune(uint32_t frontendId) {
|
|||
uint32_t demuxId;
|
||||
if (it != mFrontendToDemux.end()) {
|
||||
demuxId = it->second;
|
||||
mDemuxes[demuxId]->stopBroadcastInput();
|
||||
mDemuxes[demuxId]->stopFrontendInput();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2230,8 +2230,6 @@ struct DemuxIpFilterSettings {
|
|||
|
||||
DemuxFilterSectionSettings section;
|
||||
|
||||
DemuxFilterPesDataSettings pesData;
|
||||
|
||||
/**
|
||||
* true if the data from IP subtype go to next filter directly
|
||||
*/
|
||||
|
@ -2248,7 +2246,7 @@ struct DemuxTlvFilterSettings {
|
|||
/**
|
||||
* true if the filtered data is commpressed ip packet
|
||||
*/
|
||||
bool bIsCompressedIpPacket;
|
||||
bool isCompressedIpPacket;
|
||||
|
||||
safe_union FilterSettings {
|
||||
/**
|
||||
|
|
|
@ -65,6 +65,7 @@ using android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
|
|||
using android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterPesEvent;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterSectionEvent;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
|
||||
|
@ -95,6 +96,7 @@ using android::hardware::tv::tuner::V1_0::IFrontendCallback;
|
|||
using android::hardware::tv::tuner::V1_0::ITuner;
|
||||
using android::hardware::tv::tuner::V1_0::PlaybackSettings;
|
||||
using android::hardware::tv::tuner::V1_0::PlaybackStatus;
|
||||
using android::hardware::tv::tuner::V1_0::RecordSettings;
|
||||
using android::hardware::tv::tuner::V1_0::RecordStatus;
|
||||
using android::hardware::tv::tuner::V1_0::Result;
|
||||
|
||||
|
@ -379,7 +381,20 @@ bool FilterCallback::readFilterEventData() {
|
|||
|
||||
class DvrCallback : public IDvrCallback {
|
||||
public:
|
||||
virtual Return<void> onRecordStatus(RecordStatus /*status*/) override { return Void(); }
|
||||
virtual Return<void> onRecordStatus(DemuxFilterStatus status) override {
|
||||
ALOGW("[vts] record status %hhu", status);
|
||||
switch (status) {
|
||||
case DemuxFilterStatus::DATA_READY:
|
||||
break;
|
||||
case DemuxFilterStatus::LOW_WATER:
|
||||
break;
|
||||
case DemuxFilterStatus::HIGH_WATER:
|
||||
case DemuxFilterStatus::OVERFLOW:
|
||||
ALOGW("[vts] record overflow. Flushing");
|
||||
break;
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
virtual Return<void> onPlaybackStatus(PlaybackStatus status) override {
|
||||
// android::Mutex::Autolock autoLock(mMsgLock);
|
||||
|
@ -401,10 +416,17 @@ class DvrCallback : public IDvrCallback {
|
|||
|
||||
void testFilterDataOutput();
|
||||
void stopPlaybackThread();
|
||||
void testRecordOutput();
|
||||
void stopRecordThread();
|
||||
|
||||
void startPlaybackInputThread(PlaybackConf playbackConf, MQDesc& playbackMQDescriptor);
|
||||
void startRecordOutputThread(RecordSettings recordSetting, MQDesc& recordMQDescriptor);
|
||||
static void* __threadLoopPlayback(void* threadArgs);
|
||||
static void* __threadLoopRecord(void* threadArgs);
|
||||
void playbackThreadLoop(PlaybackConf* playbackConf, bool* keepWritingPlaybackFMQ);
|
||||
void recordThreadLoop(RecordSettings* recordSetting, bool* keepWritingPlaybackFMQ);
|
||||
|
||||
bool readRecordFMQ();
|
||||
|
||||
private:
|
||||
struct PlaybackThreadArgs {
|
||||
|
@ -412,22 +434,31 @@ class DvrCallback : public IDvrCallback {
|
|||
PlaybackConf* playbackConf;
|
||||
bool* keepWritingPlaybackFMQ;
|
||||
};
|
||||
struct RecordThreadArgs {
|
||||
DvrCallback* user;
|
||||
RecordSettings* recordSetting;
|
||||
bool* keepReadingRecordFMQ;
|
||||
};
|
||||
uint16_t mDataLength = 0;
|
||||
std::vector<uint8_t> mDataOutputBuffer;
|
||||
|
||||
std::map<uint32_t, std::unique_ptr<FilterMQ>> mFilterIdToMQ;
|
||||
std::unique_ptr<FilterMQ> mPlaybackMQ;
|
||||
std::unique_ptr<FilterMQ> mRecordMQ;
|
||||
std::map<uint32_t, EventFlag*> mFilterIdToMQEventFlag;
|
||||
std::map<uint32_t, DemuxFilterEvent> mFilterIdToEvent;
|
||||
EventFlag* mPlaybackMQEventFlag;
|
||||
|
||||
android::Mutex mMsgLock;
|
||||
android::Mutex mPlaybackThreadLock;
|
||||
android::Mutex mRecordThreadLock;
|
||||
android::Condition mMsgCondition;
|
||||
|
||||
bool mKeepWritingPlaybackFMQ = true;
|
||||
bool mKeepReadingRecordFMQ = true;
|
||||
bool mPlaybackThreadRunning;
|
||||
bool mRecordThreadRunning;
|
||||
pthread_t mPlaybackThread;
|
||||
pthread_t mRecordThread;
|
||||
|
||||
int mPidFilterOutputCount = 0;
|
||||
};
|
||||
|
@ -516,6 +547,92 @@ void DvrCallback::playbackThreadLoop(PlaybackConf* playbackConf, bool* keepWriti
|
|||
inputData.close();
|
||||
}
|
||||
|
||||
void DvrCallback::testRecordOutput() {
|
||||
android::Mutex::Autolock autoLock(mMsgLock);
|
||||
while (mDataOutputBuffer.empty()) {
|
||||
if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
|
||||
EXPECT_TRUE(false) << "record output matching pid does not output within timeout";
|
||||
return;
|
||||
}
|
||||
}
|
||||
stopRecordThread();
|
||||
ALOGW("[vts] record pass and stop");
|
||||
}
|
||||
|
||||
void DvrCallback::startRecordOutputThread(RecordSettings recordSetting,
|
||||
MQDesc& recordMQDescriptor) {
|
||||
mRecordMQ = std::make_unique<FilterMQ>(recordMQDescriptor, true /* resetPointers */);
|
||||
EXPECT_TRUE(mRecordMQ);
|
||||
struct RecordThreadArgs* threadArgs =
|
||||
(struct RecordThreadArgs*)malloc(sizeof(struct RecordThreadArgs));
|
||||
threadArgs->user = this;
|
||||
threadArgs->recordSetting = &recordSetting;
|
||||
threadArgs->keepReadingRecordFMQ = &mKeepReadingRecordFMQ;
|
||||
|
||||
pthread_create(&mRecordThread, NULL, __threadLoopRecord, (void*)threadArgs);
|
||||
pthread_setname_np(mRecordThread, "test_record_input_loop");
|
||||
}
|
||||
|
||||
void* DvrCallback::__threadLoopRecord(void* threadArgs) {
|
||||
DvrCallback* const self =
|
||||
static_cast<DvrCallback*>(((struct RecordThreadArgs*)threadArgs)->user);
|
||||
self->recordThreadLoop(((struct RecordThreadArgs*)threadArgs)->recordSetting,
|
||||
((struct RecordThreadArgs*)threadArgs)->keepReadingRecordFMQ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DvrCallback::recordThreadLoop(RecordSettings* /*recordSetting*/, bool* keepReadingRecordFMQ) {
|
||||
ALOGD("[vts] DvrCallback record threadLoop start.");
|
||||
android::Mutex::Autolock autoLock(mRecordThreadLock);
|
||||
mRecordThreadRunning = true;
|
||||
|
||||
// Create the EventFlag that is used to signal the HAL impl that data have been
|
||||
// read from the Record FMQ
|
||||
EventFlag* recordMQEventFlag;
|
||||
EXPECT_TRUE(EventFlag::createEventFlag(mRecordMQ->getEventFlagWord(), &recordMQEventFlag) ==
|
||||
android::OK);
|
||||
|
||||
while (mRecordThreadRunning) {
|
||||
while (*keepReadingRecordFMQ) {
|
||||
uint32_t efState = 0;
|
||||
android::status_t status = recordMQEventFlag->wait(
|
||||
static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY), &efState, WAIT_TIMEOUT,
|
||||
true /* retry on spurious wake */);
|
||||
if (status != android::OK) {
|
||||
ALOGD("[vts] wait for data ready on the record FMQ");
|
||||
continue;
|
||||
}
|
||||
// Our current implementation filter the data and write it into the filter FMQ
|
||||
// immediately after the DATA_READY from the VTS/framework
|
||||
if (!readRecordFMQ()) {
|
||||
ALOGD("[vts] record data failed to be filtered. Ending thread");
|
||||
mRecordThreadRunning = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mRecordThreadRunning = false;
|
||||
ALOGD("[vts] record thread ended.");
|
||||
}
|
||||
|
||||
bool DvrCallback::readRecordFMQ() {
|
||||
android::Mutex::Autolock autoLock(mMsgLock);
|
||||
bool result = false;
|
||||
mDataOutputBuffer.clear();
|
||||
mDataOutputBuffer.resize(mRecordMQ->availableToRead());
|
||||
result = mRecordMQ->read(mDataOutputBuffer.data(), mRecordMQ->availableToRead());
|
||||
EXPECT_TRUE(result) << "can't read from Record MQ";
|
||||
mMsgCondition.signal();
|
||||
return result;
|
||||
}
|
||||
|
||||
void DvrCallback::stopRecordThread() {
|
||||
mKeepReadingRecordFMQ = false;
|
||||
mRecordThreadRunning = false;
|
||||
android::Mutex::Autolock autoLock(mRecordThreadLock);
|
||||
}
|
||||
|
||||
// Test environment for Tuner HIDL HAL.
|
||||
class TunerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
|
||||
public:
|
||||
|
@ -555,6 +672,7 @@ class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
|
|||
sp<DvrCallback> mDvrCallback;
|
||||
MQDesc mFilterMQDescriptor;
|
||||
MQDesc mPlaybackMQDescriptor;
|
||||
MQDesc mRecordMQDescriptor;
|
||||
vector<uint32_t> mUsedFilterIds;
|
||||
|
||||
uint32_t mDemuxId;
|
||||
|
@ -572,6 +690,8 @@ class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
|
|||
FrontendSettings settings);
|
||||
::testing::AssertionResult getPlaybackMQDescriptor();
|
||||
::testing::AssertionResult addPlaybackToDemux(PlaybackSettings setting);
|
||||
::testing::AssertionResult getRecordMQDescriptor();
|
||||
::testing::AssertionResult addRecordToDemux(RecordSettings setting);
|
||||
::testing::AssertionResult addFilterToDemux(DemuxFilterType type, DemuxFilterSettings setting);
|
||||
::testing::AssertionResult getFilterMQDescriptor();
|
||||
::testing::AssertionResult closeDemux();
|
||||
|
@ -581,6 +701,9 @@ class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
|
|||
::testing::AssertionResult playbackDataFlowTest(vector<FilterConf> filterConf,
|
||||
PlaybackConf playbackConf,
|
||||
vector<string> goldenOutputFiles);
|
||||
::testing::AssertionResult recordDataFlowTest(vector<FilterConf> filterConf,
|
||||
RecordSettings recordSetting,
|
||||
vector<string> goldenOutputFiles);
|
||||
::testing::AssertionResult broadcastDataFlowTest(vector<FilterConf> filterConf,
|
||||
vector<string> goldenOutputFiles);
|
||||
};
|
||||
|
@ -766,6 +889,49 @@ class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
|
|||
return ::testing::AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
|
||||
::testing::AssertionResult TunerHidlTest::addRecordToDemux(RecordSettings setting) {
|
||||
Result status;
|
||||
|
||||
if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
|
||||
// Create dvr callback
|
||||
mDvrCallback = new DvrCallback();
|
||||
|
||||
// Add playback input to the local demux
|
||||
mDemux->openDvr(DvrType::RECORD, FMQ_SIZE_1M, mDvrCallback,
|
||||
[&](Result result, const sp<IDvr>& dvr) {
|
||||
mDvr = dvr;
|
||||
status = result;
|
||||
});
|
||||
|
||||
if (status != Result::SUCCESS) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
|
||||
DvrSettings dvrSetting;
|
||||
dvrSetting.record(setting);
|
||||
status = mDvr->configure(dvrSetting);
|
||||
|
||||
return ::testing::AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
|
||||
::testing::AssertionResult TunerHidlTest::getRecordMQDescriptor() {
|
||||
Result status;
|
||||
|
||||
if ((!mDemux && createDemux() == ::testing::AssertionFailure()) || !mDvr) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
|
||||
mDvr->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
|
||||
mRecordMQDescriptor = dvrMQDesc;
|
||||
status = result;
|
||||
});
|
||||
|
||||
return ::testing::AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
|
||||
::testing::AssertionResult TunerHidlTest::addFilterToDemux(DemuxFilterType type,
|
||||
DemuxFilterSettings setting) {
|
||||
Result status;
|
||||
|
@ -997,6 +1163,82 @@ class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
|
|||
return closeDemux();
|
||||
}
|
||||
|
||||
::testing::AssertionResult TunerHidlTest::recordDataFlowTest(vector<FilterConf> filterConf,
|
||||
RecordSettings recordSetting,
|
||||
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);
|
||||
|
||||
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() == ::testing::AssertionFailure()) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
filterIdsSize = mUsedFilterIds.size();
|
||||
mUsedFilterIds.resize(filterIdsSize + 1);
|
||||
mUsedFilterIds[filterIdsSize] = mFilterId;
|
||||
mFilters[mFilterId] = mFilter;
|
||||
}
|
||||
|
||||
// Record Config Module
|
||||
if (addRecordToDemux(recordSetting) == ::testing::AssertionFailure() ||
|
||||
getRecordMQDescriptor() == ::testing::AssertionFailure()) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
for (int i = 0; i <= filterIdsSize; i++) {
|
||||
if (mDvr->attachFilter(mFilters[mUsedFilterIds[i]]) != Result::SUCCESS) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
}
|
||||
|
||||
mDvrCallback->startRecordOutputThread(recordSetting, mRecordMQDescriptor);
|
||||
status = mDvr->start();
|
||||
if (status != Result::SUCCESS) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
|
||||
if (createDemuxWithFrontend(feIds[0], settings) != ::testing::AssertionSuccess()) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
|
||||
// Data Verify Module
|
||||
mDvrCallback->testRecordOutput();
|
||||
|
||||
// Clean Up Module
|
||||
for (int i = 0; i <= filterIdsSize; i++) {
|
||||
if (mFilters[mUsedFilterIds[i]]->stop() != Result::SUCCESS) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
}
|
||||
if (mFrontend->stopTune() != Result::SUCCESS) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
mUsedFilterIds.clear();
|
||||
mFilterCallbacks.clear();
|
||||
mFilters.clear();
|
||||
return closeDemux();
|
||||
}
|
||||
|
||||
/*
|
||||
* API STATUS TESTS
|
||||
*/
|
||||
|
@ -1203,6 +1445,44 @@ TEST_F(TunerHidlTest, BroadcastDataFlowWithPesFilterTest) {
|
|||
ASSERT_TRUE(broadcastDataFlowTest(filterConf, goldenOutputFiles));
|
||||
}
|
||||
|
||||
TEST_F(TunerHidlTest, RecordDataFlowWithTsRecordFilterTest) {
|
||||
description("Feed ts data from frontend to recording and test with ts record filter");
|
||||
|
||||
// todo modulize the filter conf parser
|
||||
vector<FilterConf> filterConf;
|
||||
filterConf.resize(1);
|
||||
|
||||
DemuxFilterSettings filterSetting;
|
||||
DemuxTsFilterSettings tsFilterSetting{
|
||||
.tpid = 119,
|
||||
};
|
||||
DemuxFilterRecordSettings recordFilterSetting;
|
||||
tsFilterSetting.filterSettings.record(recordFilterSetting);
|
||||
filterSetting.ts(tsFilterSetting);
|
||||
|
||||
DemuxFilterType type{
|
||||
.mainType = DemuxFilterMainType::TS,
|
||||
};
|
||||
type.subType.tsFilterType(DemuxTsFilterType::RECORD);
|
||||
FilterConf recordFilterConf{
|
||||
.type = type,
|
||||
.setting = filterSetting,
|
||||
};
|
||||
filterConf[0] = recordFilterConf;
|
||||
|
||||
RecordSettings recordSetting{
|
||||
.statusMask = 0xf,
|
||||
.lowThreshold = 0x1000,
|
||||
.highThreshold = 0x07fff,
|
||||
.dataFormat = DataFormat::TS,
|
||||
.packetSize = 188,
|
||||
};
|
||||
|
||||
vector<string> goldenOutputFiles;
|
||||
|
||||
ASSERT_TRUE(recordDataFlowTest(filterConf, recordSetting, goldenOutputFiles));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
|
Loading…
Reference in a new issue