platform_hardware_interfaces/tv/tuner/1.0/default/Dvr.cpp
Amy 5ed13574aa Adding a DVR Record default implementation in Tuner HAL 1.0
Test: cuttlefish
Bug: 135709325
Change-Id: I415426d6ec048bdd2ae61a3c5142ad02f1d7f1e4
2019-12-17 16:06:25 -08:00

341 lines
No EOL
9.8 KiB
C++

/*
* 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.
*/
#define LOG_TAG "android.hardware.tv.tuner@1.0-Dvr"
#include "Dvr.h"
#include <utils/Log.h>
namespace android {
namespace hardware {
namespace tv {
namespace tuner {
namespace V1_0 {
namespace implementation {
#define WAIT_TIMEOUT 3000000000
Dvr::Dvr() {}
Dvr::Dvr(DvrType type, uint32_t bufferSize, const sp<IDvrCallback>& cb, sp<Demux> demux) {
mType = type;
mBufferSize = bufferSize;
mCallback = cb;
mDemux = demux;
}
Dvr::~Dvr() {}
Return<void> Dvr::getQueueDesc(getQueueDesc_cb _hidl_cb) {
ALOGV("%s", __FUNCTION__);
_hidl_cb(Result::SUCCESS, *mDvrMQ->getDesc());
return Void();
}
Return<Result> Dvr::configure(const DvrSettings& settings) {
ALOGV("%s", __FUNCTION__);
mDvrSettings = settings;
mDvrConfigured = true;
return Result::SUCCESS;
}
Return<Result> Dvr::attachFilter(const sp<IFilter>& filter) {
ALOGV("%s", __FUNCTION__);
uint32_t filterId;
Result status;
filter->getId([&](Result result, uint32_t id) {
filterId = id;
status = result;
});
if (status != Result::SUCCESS) {
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;
}
Return<Result> Dvr::detachFilter(const sp<IFilter>& filter) {
ALOGV("%s", __FUNCTION__);
uint32_t filterId;
Result status;
filter->getId([&](Result result, uint32_t id) {
filterId = id;
status = result;
});
if (status != Result::SUCCESS) {
return status;
}
std::map<uint32_t, sp<IFilter>>::iterator it;
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;
}
Return<Result> Dvr::start() {
ALOGV("%s", __FUNCTION__);
if (!mCallback) {
return Result::NOT_INITIALIZED;
}
if (!mDvrConfigured) {
return Result::INVALID_STATE;
}
if (mType == DvrType::PLAYBACK) {
pthread_create(&mDvrThread, NULL, __threadLoopPlayback, this);
pthread_setname_np(mDvrThread, "playback_waiting_loop");
} else if (mType == DvrType::RECORD) {
mRecordStatus = RecordStatus::DATA_READY;
mIsRecordStarted = true;
mDemux->setIsRecording(mIsRecordStarted | mIsRecordFilterAttached);
}
// TODO start another thread to send filter status callback to the framework
return Result::SUCCESS;
}
Return<Result> Dvr::stop() {
ALOGV("%s", __FUNCTION__);
mDvrThreadRunning = false;
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;
}
Return<Result> Dvr::close() {
ALOGV("%s", __FUNCTION__);
return Result::SUCCESS;
}
bool Dvr::createDvrMQ() {
ALOGV("%s", __FUNCTION__);
// Create a synchronized FMQ that supports blocking read/write
std::unique_ptr<DvrMQ> tmpDvrMQ =
std::unique_ptr<DvrMQ>(new (std::nothrow) DvrMQ(mBufferSize, true));
if (!tmpDvrMQ->isValid()) {
ALOGW("Failed to create FMQ of DVR");
return false;
}
mDvrMQ = std::move(tmpDvrMQ);
if (EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrEventFlag) != OK) {
return false;
}
return true;
}
void* Dvr::__threadLoopPlayback(void* user) {
Dvr* const self = static_cast<Dvr*>(user);
self->playbackThreadLoop();
return 0;
}
void Dvr::playbackThreadLoop() {
ALOGD("[Dvr] playback threadLoop start.");
std::lock_guard<std::mutex> lock(mDvrThreadLock);
mDvrThreadRunning = true;
while (mDvrThreadRunning) {
uint32_t efState = 0;
status_t status =
mDvrEventFlag->wait(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY),
&efState, WAIT_TIMEOUT, true /* retry on spurious wake */);
if (status != OK) {
ALOGD("[Dvr] wait for data ready on the playback 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 (!readPlaybackFMQ() || !startFilterDispatcher()) {
ALOGD("[Dvr] playback data failed to be filtered. Ending thread");
break;
}
maySendPlaybackStatusCallback();
}
mDvrThreadRunning = false;
ALOGD("[Dvr] playback thread ended.");
}
void Dvr::maySendPlaybackStatusCallback() {
std::lock_guard<std::mutex> lock(mPlaybackStatusLock);
int availableToRead = mDvrMQ->availableToRead();
int availableToWrite = mDvrMQ->availableToWrite();
PlaybackStatus newStatus = checkPlaybackStatusChange(availableToWrite, availableToRead,
mDvrSettings.playback().highThreshold,
mDvrSettings.playback().lowThreshold);
if (mPlaybackStatus != newStatus) {
mCallback->onPlaybackStatus(newStatus);
mPlaybackStatus = newStatus;
}
}
PlaybackStatus Dvr::checkPlaybackStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
uint32_t highThreshold, uint32_t lowThreshold) {
if (availableToWrite == 0) {
return PlaybackStatus::SPACE_FULL;
} else if (availableToRead > highThreshold) {
return PlaybackStatus::SPACE_ALMOST_FULL;
} else if (availableToRead < lowThreshold) {
return PlaybackStatus::SPACE_ALMOST_EMPTY;
} else if (availableToRead == 0) {
return PlaybackStatus::SPACE_EMPTY;
}
return mPlaybackStatus;
}
bool Dvr::readPlaybackFMQ() {
// Read playback data from the input FMQ
int size = mDvrMQ->availableToRead();
int playbackPacketSize = mDvrSettings.playback().packetSize;
vector<uint8_t> dataOutputBuffer;
dataOutputBuffer.resize(playbackPacketSize);
// Dispatch the packet to the PID matching filter output buffer
for (int i = 0; i < size / playbackPacketSize; i++) {
if (!mDvrMQ->read(dataOutputBuffer.data(), playbackPacketSize)) {
return false;
}
startTpidFilter(dataOutputBuffer);
}
return true;
}
void Dvr::startTpidFilter(vector<uint8_t> data) {
std::map<uint32_t, sp<IFilter>>::iterator it;
for (it = mFilters.begin(); it != mFilters.end(); it++) {
uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff));
if (DEBUG_DVR) {
ALOGW("[Dvr] start ts filter pid: %d", pid);
}
if (pid == mDemux->getFilterTpid(it->first)) {
mDemux->updateFilterOutput(it->first, data);
}
}
}
bool Dvr::startFilterDispatcher() {
std::map<uint32_t, sp<IFilter>>::iterator it;
// Handle the output data per filter type
for (it = mFilters.begin(); it != mFilters.end(); it++) {
if (mDemux->startFilterHandler(it->first) != Result::SUCCESS) {
return false;
}
}
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
} // namespace tv
} // namespace hardware
} // namespace android