| /* |
| * Copyright 2021 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" |
| |
| #include <aidl/android/hardware/tv/tuner/DemuxQueueNotifyBits.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); |
| |
| mPlaybackThread = std::thread(&DvrCallback::playbackThreadLoop, this); |
| } |
| |
| void DvrCallback::stopPlaybackThread() { |
| mPlaybackThreadRunning = false; |
| mKeepWritingPlaybackFMQ = false; |
| |
| if (mPlaybackThread.joinable()) { |
| mPlaybackThread.join(); |
| } |
| } |
| |
| void DvrCallback::playbackThreadLoop() { |
| mPlaybackThreadRunning = true; |
| mKeepWritingPlaybackFMQ = 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; |
| int8_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; |
| } |
| AidlMessageQueue<int8_t, SynchronizedReadWrite>::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() { |
| bool passed = true; |
| { |
| 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"; |
| passed = false; |
| break; |
| } |
| } |
| } |
| stopRecordThread(); |
| if (passed) ALOGW("[vts] record pass and stop"); |
| } |
| |
| void DvrCallback::startRecordOutputThread(RecordSettings /* recordSettings */, |
| MQDesc& recordMQDescriptor) { |
| mRecordMQ = std::make_unique<FilterMQ>(recordMQDescriptor, true /* resetPointers */); |
| EXPECT_TRUE(mRecordMQ); |
| |
| mRecordThread = std::thread(&DvrCallback::recordThreadLoop, this); |
| } |
| |
| void DvrCallback::recordThreadLoop() { |
| ALOGD("[vts] DvrCallback record threadLoop start."); |
| 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 (mKeepReadingRecordFMQ) { |
| uint32_t efState = 0; |
| android::status_t status = recordMQEventFlag->wait( |
| static_cast<int32_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; |
| |
| if (mRecordThread.joinable()) { |
| mRecordThread.join(); |
| } |
| } |
| |
| AssertionResult DvrTests::openDvrInDemux(DvrType type, int32_t bufferSize) { |
| ndk::ScopedAStatus status; |
| EXPECT_TRUE(mDemux) << "Test with openDemux first."; |
| |
| // Create dvr callback |
| if (type == DvrType::PLAYBACK) { |
| mDvrPlaybackCallback = ndk::SharedRefBase::make<DvrCallback>(); |
| status = mDemux->openDvr(type, bufferSize, mDvrPlaybackCallback, &mDvrPlayback); |
| if (status.isOk()) { |
| mDvrPlaybackCallback->setDvr(mDvrPlayback); |
| } |
| } |
| |
| if (type == DvrType::RECORD) { |
| mDvrRecordCallback = ndk::SharedRefBase::make<DvrCallback>(); |
| status = mDemux->openDvr(type, bufferSize, mDvrRecordCallback, &mDvrRecord); |
| if (status.isOk()) { |
| mDvrRecordCallback->setDvr(mDvrRecord); |
| } |
| } |
| |
| return AssertionResult(status.isOk()); |
| } |
| |
| AssertionResult DvrTests::configDvrPlayback(DvrSettings setting) { |
| ndk::ScopedAStatus status = mDvrPlayback->configure(setting); |
| |
| return AssertionResult(status.isOk()); |
| } |
| |
| AssertionResult DvrTests::configDvrRecord(DvrSettings setting) { |
| ndk::ScopedAStatus status = mDvrRecord->configure(setting); |
| |
| return AssertionResult(status.isOk()); |
| } |
| |
| AssertionResult DvrTests::getDvrPlaybackMQDescriptor() { |
| ndk::ScopedAStatus status; |
| EXPECT_TRUE(mDemux) << "Test with openDemux first."; |
| EXPECT_TRUE(mDvrPlayback) << "Test with openDvr first."; |
| |
| status = mDvrPlayback->getQueueDesc(&mDvrPlaybackMQDescriptor); |
| |
| return AssertionResult(status.isOk()); |
| } |
| |
| AssertionResult DvrTests::getDvrRecordMQDescriptor() { |
| ndk::ScopedAStatus status; |
| EXPECT_TRUE(mDemux) << "Test with openDemux first."; |
| EXPECT_TRUE(mDvrRecord) << "Test with openDvr first."; |
| |
| status = mDvrRecord->getQueueDesc(&mDvrRecordMQDescriptor); |
| |
| return AssertionResult(status.isOk()); |
| } |
| |
| AssertionResult DvrTests::attachFilterToDvr(std::shared_ptr<IFilter> filter) { |
| ndk::ScopedAStatus status; |
| EXPECT_TRUE(mDemux) << "Test with openDemux first."; |
| EXPECT_TRUE(mDvrRecord) << "Test with openDvr first."; |
| |
| status = mDvrRecord->attachFilter(filter); |
| |
| return AssertionResult(status.isOk()); |
| } |
| |
| AssertionResult DvrTests::detachFilterToDvr(std::shared_ptr<IFilter> filter) { |
| ndk::ScopedAStatus status; |
| EXPECT_TRUE(mDemux) << "Test with openDemux first."; |
| EXPECT_TRUE(mDvrRecord) << "Test with openDvr first."; |
| |
| status = mDvrRecord->detachFilter(filter); |
| |
| return AssertionResult(status.isOk()); |
| } |
| |
| AssertionResult DvrTests::startDvrPlayback() { |
| ndk::ScopedAStatus status; |
| EXPECT_TRUE(mDemux) << "Test with openDemux first."; |
| EXPECT_TRUE(mDvrPlayback) << "Test with openDvr first."; |
| |
| status = mDvrPlayback->start(); |
| |
| return AssertionResult(status.isOk()); |
| } |
| |
| AssertionResult DvrTests::stopDvrPlayback() { |
| ndk::ScopedAStatus status; |
| EXPECT_TRUE(mDemux) << "Test with openDemux first."; |
| EXPECT_TRUE(mDvrPlayback) << "Test with openDvr first."; |
| |
| status = mDvrPlayback->stop(); |
| |
| return AssertionResult(status.isOk()); |
| } |
| |
| void DvrTests::closeDvrPlayback() { |
| ASSERT_TRUE(mDemux); |
| ASSERT_TRUE(mDvrPlayback); |
| ASSERT_TRUE(mDvrPlayback->close().isOk()); |
| } |
| |
| AssertionResult DvrTests::startDvrRecord() { |
| ndk::ScopedAStatus status; |
| EXPECT_TRUE(mDemux) << "Test with openDemux first."; |
| EXPECT_TRUE(mDvrRecord) << "Test with openDvr first."; |
| |
| status = mDvrRecord->start(); |
| |
| return AssertionResult(status.isOk()); |
| } |
| |
| AssertionResult DvrTests::stopDvrRecord() { |
| ndk::ScopedAStatus status; |
| EXPECT_TRUE(mDemux) << "Test with openDemux first."; |
| EXPECT_TRUE(mDvrRecord) << "Test with openDvr first."; |
| |
| status = mDvrRecord->stop(); |
| |
| return AssertionResult(status.isOk()); |
| } |
| |
| void DvrTests::closeDvrRecord() { |
| ASSERT_TRUE(mDemux); |
| ASSERT_TRUE(mDvrRecord); |
| ASSERT_TRUE(mDvrRecord->close().isOk()); |
| } |