You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
350 lines
12 KiB
350 lines
12 KiB
/*
|
|
* Copyright 2020 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.
|
|
*/
|
|
|
|
#include "DvrTests.h"
|
|
|
|
void DvrCallback::startPlaybackInputThread(string& dataInputFile, PlaybackSettings& settings,
|
|
MQDesc& playbackMQDescriptor) {
|
|
mInputDataFile = dataInputFile;
|
|
mPlaybackSettings = settings;
|
|
mPlaybackMQ = std::make_unique<FilterMQ>(playbackMQDescriptor, true /* resetPointers */);
|
|
EXPECT_TRUE(mPlaybackMQ);
|
|
pthread_create(&mPlaybackThread, NULL, __threadLoopPlayback, this);
|
|
pthread_setname_np(mPlaybackThread, "test_playback_input_loop");
|
|
}
|
|
|
|
void DvrCallback::stopPlaybackThread() {
|
|
mPlaybackThreadRunning = false;
|
|
mKeepWritingPlaybackFMQ = false;
|
|
|
|
android::Mutex::Autolock autoLock(mPlaybackThreadLock);
|
|
}
|
|
|
|
void* DvrCallback::__threadLoopPlayback(void* user) {
|
|
DvrCallback* const self = static_cast<DvrCallback*>(user);
|
|
self->playbackThreadLoop();
|
|
return 0;
|
|
}
|
|
|
|
void DvrCallback::playbackThreadLoop() {
|
|
android::Mutex::Autolock autoLock(mPlaybackThreadLock);
|
|
mPlaybackThreadRunning = true;
|
|
|
|
// Create the EventFlag that is used to signal the HAL impl that data have been
|
|
// written into the Playback FMQ
|
|
EventFlag* playbackMQEventFlag;
|
|
EXPECT_TRUE(EventFlag::createEventFlag(mPlaybackMQ->getEventFlagWord(), &playbackMQEventFlag) ==
|
|
android::OK);
|
|
|
|
int fd = open(mInputDataFile.c_str(), O_RDONLY | O_LARGEFILE);
|
|
int readBytes;
|
|
uint32_t regionSize = 0;
|
|
uint8_t* buffer;
|
|
ALOGW("[vts] playback thread loop start %s", mInputDataFile.c_str());
|
|
if (fd < 0) {
|
|
mPlaybackThreadRunning = false;
|
|
ALOGW("[vts] Error %s", strerror(errno));
|
|
}
|
|
|
|
while (mPlaybackThreadRunning) {
|
|
while (mKeepWritingPlaybackFMQ) {
|
|
int totalWrite = mPlaybackMQ->availableToWrite();
|
|
if (totalWrite * 4 < mPlaybackMQ->getQuantumCount()) {
|
|
// Wait for the HAL implementation to read more data then write.
|
|
continue;
|
|
}
|
|
MessageQueue<uint8_t, kSynchronizedReadWrite>::MemTransaction memTx;
|
|
if (!mPlaybackMQ->beginWrite(totalWrite, &memTx)) {
|
|
ALOGW("[vts] Fail to write into Playback fmq.");
|
|
mPlaybackThreadRunning = false;
|
|
break;
|
|
}
|
|
auto first = memTx.getFirstRegion();
|
|
buffer = first.getAddress();
|
|
regionSize = first.getLength();
|
|
|
|
if (regionSize > 0) {
|
|
readBytes = read(fd, buffer, regionSize);
|
|
if (readBytes <= 0) {
|
|
if (readBytes < 0) {
|
|
ALOGW("[vts] Read from %s failed.", mInputDataFile.c_str());
|
|
} else {
|
|
ALOGW("[vts] playback input EOF.");
|
|
}
|
|
mPlaybackThreadRunning = false;
|
|
break;
|
|
}
|
|
}
|
|
if (regionSize == 0 || (readBytes == regionSize && regionSize < totalWrite)) {
|
|
auto second = memTx.getSecondRegion();
|
|
buffer = second.getAddress();
|
|
regionSize = second.getLength();
|
|
int ret = read(fd, buffer, regionSize);
|
|
if (ret <= 0) {
|
|
if (ret < 0) {
|
|
ALOGW("[vts] Read from %s failed.", mInputDataFile.c_str());
|
|
} else {
|
|
ALOGW("[vts] playback input EOF.");
|
|
}
|
|
mPlaybackThreadRunning = false;
|
|
break;
|
|
}
|
|
readBytes += ret;
|
|
}
|
|
if (!mPlaybackMQ->commitWrite(readBytes)) {
|
|
ALOGW("[vts] Failed to commit write playback fmq.");
|
|
mPlaybackThreadRunning = false;
|
|
break;
|
|
}
|
|
playbackMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
|
|
}
|
|
}
|
|
|
|
mPlaybackThreadRunning = false;
|
|
ALOGW("[vts] Playback thread end.");
|
|
close(fd);
|
|
}
|
|
|
|
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";
|
|
stopRecordThread();
|
|
return;
|
|
}
|
|
}
|
|
stopRecordThread();
|
|
ALOGW("[vts] record pass and stop");
|
|
}
|
|
|
|
void DvrCallback::startRecordOutputThread(RecordSettings recordSettings,
|
|
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->recordSettings = &recordSettings;
|
|
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)->recordSettings,
|
|
((struct RecordThreadArgs*)threadArgs)->keepReadingRecordFMQ);
|
|
return 0;
|
|
}
|
|
|
|
void DvrCallback::recordThreadLoop(RecordSettings* /*recordSettings*/, bool* keepReadingRecordFMQ) {
|
|
ALOGD("[vts] DvrCallback record threadLoop start.");
|
|
android::Mutex::Autolock autoLock(mRecordThreadLock);
|
|
mRecordThreadRunning = true;
|
|
mKeepReadingRecordFMQ = 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;
|
|
int readSize = mRecordMQ->availableToRead();
|
|
mDataOutputBuffer.clear();
|
|
mDataOutputBuffer.resize(readSize);
|
|
result = mRecordMQ->read(mDataOutputBuffer.data(), readSize);
|
|
EXPECT_TRUE(result) << "can't read from Record MQ";
|
|
mMsgCondition.signal();
|
|
return result;
|
|
}
|
|
|
|
void DvrCallback::stopRecordThread() {
|
|
mKeepReadingRecordFMQ = false;
|
|
mRecordThreadRunning = false;
|
|
}
|
|
|
|
AssertionResult DvrTests::openDvrInDemux(DvrType type, uint32_t bufferSize) {
|
|
Result status;
|
|
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
|
|
|
|
// Create dvr callback
|
|
if (type == DvrType::PLAYBACK) {
|
|
mDvrPlaybackCallback = new DvrCallback();
|
|
mDemux->openDvr(type, bufferSize, mDvrPlaybackCallback,
|
|
[&](Result result, const sp<IDvr>& dvr) {
|
|
mDvrPlayback = dvr;
|
|
status = result;
|
|
});
|
|
if (status == Result::SUCCESS) {
|
|
mDvrPlaybackCallback->setDvr(mDvrPlayback);
|
|
}
|
|
}
|
|
|
|
if (type == DvrType::RECORD) {
|
|
mDvrRecordCallback = new DvrCallback();
|
|
mDemux->openDvr(type, bufferSize, mDvrRecordCallback,
|
|
[&](Result result, const sp<IDvr>& dvr) {
|
|
mDvrRecord = dvr;
|
|
status = result;
|
|
});
|
|
if (status == Result::SUCCESS) {
|
|
mDvrRecordCallback->setDvr(mDvrRecord);
|
|
}
|
|
}
|
|
|
|
return AssertionResult(status == Result::SUCCESS);
|
|
}
|
|
|
|
AssertionResult DvrTests::configDvrPlayback(DvrSettings setting) {
|
|
Result status = mDvrPlayback->configure(setting);
|
|
|
|
return AssertionResult(status == Result::SUCCESS);
|
|
}
|
|
|
|
AssertionResult DvrTests::configDvrRecord(DvrSettings setting) {
|
|
Result status = mDvrRecord->configure(setting);
|
|
|
|
return AssertionResult(status == Result::SUCCESS);
|
|
}
|
|
|
|
AssertionResult DvrTests::getDvrPlaybackMQDescriptor() {
|
|
Result status;
|
|
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
|
|
EXPECT_TRUE(mDvrPlayback) << "Test with openDvr first.";
|
|
|
|
mDvrPlayback->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
|
|
mDvrPlaybackMQDescriptor = dvrMQDesc;
|
|
status = result;
|
|
});
|
|
|
|
return AssertionResult(status == Result::SUCCESS);
|
|
}
|
|
|
|
AssertionResult DvrTests::getDvrRecordMQDescriptor() {
|
|
Result status;
|
|
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
|
|
EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
|
|
|
|
mDvrRecord->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
|
|
mDvrRecordMQDescriptor = dvrMQDesc;
|
|
status = result;
|
|
});
|
|
|
|
return AssertionResult(status == Result::SUCCESS);
|
|
}
|
|
|
|
AssertionResult DvrTests::attachFilterToDvr(sp<IFilter> filter) {
|
|
Result status;
|
|
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
|
|
EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
|
|
|
|
status = mDvrRecord->attachFilter(filter);
|
|
|
|
return AssertionResult(status == Result::SUCCESS);
|
|
}
|
|
|
|
AssertionResult DvrTests::detachFilterToDvr(sp<IFilter> filter) {
|
|
Result status;
|
|
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
|
|
EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
|
|
|
|
status = mDvrRecord->detachFilter(filter);
|
|
|
|
return AssertionResult(status == Result::SUCCESS);
|
|
}
|
|
|
|
AssertionResult DvrTests::startDvrPlayback() {
|
|
Result status;
|
|
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
|
|
EXPECT_TRUE(mDvrPlayback) << "Test with openDvr first.";
|
|
|
|
status = mDvrPlayback->start();
|
|
|
|
return AssertionResult(status == Result::SUCCESS);
|
|
}
|
|
|
|
AssertionResult DvrTests::stopDvrPlayback() {
|
|
Result status;
|
|
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
|
|
EXPECT_TRUE(mDvrPlayback) << "Test with openDvr first.";
|
|
|
|
status = mDvrPlayback->stop();
|
|
|
|
return AssertionResult(status == Result::SUCCESS);
|
|
}
|
|
|
|
void DvrTests::closeDvrPlayback() {
|
|
ASSERT_TRUE(mDemux);
|
|
ASSERT_TRUE(mDvrPlayback);
|
|
ASSERT_TRUE(mDvrPlayback->close() == Result::SUCCESS);
|
|
}
|
|
|
|
AssertionResult DvrTests::startDvrRecord() {
|
|
Result status;
|
|
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
|
|
EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
|
|
|
|
status = mDvrRecord->start();
|
|
|
|
return AssertionResult(status == Result::SUCCESS);
|
|
}
|
|
|
|
AssertionResult DvrTests::stopDvrRecord() {
|
|
Result status;
|
|
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
|
|
EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
|
|
|
|
status = mDvrRecord->stop();
|
|
|
|
return AssertionResult(status == Result::SUCCESS);
|
|
}
|
|
|
|
void DvrTests::closeDvrRecord() {
|
|
ASSERT_TRUE(mDemux);
|
|
ASSERT_TRUE(mDvrRecord);
|
|
ASSERT_TRUE(mDvrRecord->close() == Result::SUCCESS);
|
|
}
|