/* * 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. */ //#define LOG_NDEBUG 0 #define LOG_TAG "DvrClient" #include "DvrClient.h" #include #include #include #include #include #include #include "ClientHelper.h" using ::aidl::android::hardware::tv::tuner::DemuxQueueNotifyBits; namespace android { /////////////// DvrClient /////////////////////// DvrClient::DvrClient(shared_ptr tunerDvr) { mTunerDvr = tunerDvr; mFd = -1; mDvrMQ = nullptr; mDvrMQEventFlag = nullptr; } DvrClient::~DvrClient() { mTunerDvr = nullptr; mFd = -1; mDvrMQ = nullptr; mDvrMQEventFlag = nullptr; } void DvrClient::setFd(int32_t fd) { mFd = fd; } int64_t DvrClient::readFromFile(int64_t size) { if (mDvrMQ == nullptr || mDvrMQEventFlag == nullptr) { ALOGE("Failed to readFromFile. DVR mq is not configured"); return -1; } if (mFd < 0) { ALOGE("Failed to readFromFile. File is not configured"); return -1; } int64_t available = mDvrMQ->availableToWrite(); int64_t write = min(size, available); AidlMQ::MemTransaction tx; int64_t ret = 0; if (mDvrMQ->beginWrite(write, &tx)) { auto first = tx.getFirstRegion(); auto data = first.getAddress(); int64_t length = first.getLength(); int64_t firstToWrite = min(length, write); ret = read(mFd, data, firstToWrite); if (ret < 0) { ALOGE("Failed to read from FD: %s", strerror(errno)); return -1; } if (ret < firstToWrite) { ALOGW("file to MQ, first region: %" PRIu64 " bytes to write, but %" PRIu64 " bytes written", firstToWrite, ret); } else if (firstToWrite < write) { ALOGV("write second region: %" PRIu64 " bytes written, %" PRIu64 " bytes in total", ret, write); auto second = tx.getSecondRegion(); data = second.getAddress(); length = second.getLength(); int64_t secondToWrite = std::min(length, write - firstToWrite); ret += read(mFd, data, secondToWrite); } ALOGV("file to MQ: %" PRIu64 " bytes need to be written, %" PRIu64 " bytes written", write, ret); if (!mDvrMQ->commitWrite(ret)) { ALOGE("Error: failed to commit write!"); return -1; } } else { ALOGE("dvrMq.beginWrite failed"); } if (ret > 0) { mDvrMQEventFlag->wake(static_cast(DemuxQueueNotifyBits::DATA_READY)); } return ret; } int64_t DvrClient::readFromBuffer(int8_t* buffer, int64_t size) { if (mDvrMQ == nullptr || mDvrMQEventFlag == nullptr) { ALOGE("Failed to readFromBuffer. DVR mq is not configured"); return -1; } if (buffer == nullptr) { ALOGE("Failed to readFromBuffer. Buffer can't be null"); return -1; } int64_t available = mDvrMQ->availableToWrite(); size = min(size, available); if (mDvrMQ->write(buffer, size)) { mDvrMQEventFlag->wake(static_cast(DemuxQueueNotifyBits::DATA_READY)); } else { ALOGD("Failed to write FMQ"); return -1; } return size; } int64_t DvrClient::writeToFile(int64_t size) { if (mDvrMQ == nullptr || mDvrMQEventFlag == nullptr) { ALOGE("Failed to writeToFile. DVR mq is not configured"); return -1; } if (mFd < 0) { ALOGE("Failed to writeToFile. File is not configured"); return -1; } int64_t available = mDvrMQ->availableToRead(); int64_t toRead = min(size, available); int64_t ret = 0; AidlMQ::MemTransaction tx; if (mDvrMQ->beginRead(toRead, &tx)) { auto first = tx.getFirstRegion(); auto data = first.getAddress(); int64_t length = first.getLength(); int64_t firstToRead = std::min(length, toRead); ret = write(mFd, data, firstToRead); if (ret < 0) { ALOGE("Failed to write to FD: %s", strerror(errno)); return -1; } if (ret < firstToRead) { ALOGW("MQ to file: %" PRIu64 " bytes read, but %" PRIu64 " bytes written", firstToRead, ret); } else if (firstToRead < toRead) { ALOGV("read second region: %" PRIu64 " bytes read, %" PRIu64 " bytes in total", ret, toRead); auto second = tx.getSecondRegion(); data = second.getAddress(); length = second.getLength(); int32_t secondToRead = toRead - firstToRead; ret += write(mFd, data, secondToRead); } ALOGV("MQ to file: %" PRIu64 " bytes to be read, %" PRIu64 " bytes written", toRead, ret); if (!mDvrMQ->commitRead(ret)) { ALOGE("Error: failed to commit read!"); return 0; } } else { ALOGE("dvrMq.beginRead failed"); } if (ret > 0) { mDvrMQEventFlag->wake(static_cast(DemuxQueueNotifyBits::DATA_CONSUMED)); } return ret; } int64_t DvrClient::writeToBuffer(int8_t* buffer, int64_t size) { if (mDvrMQ == nullptr || mDvrMQEventFlag == nullptr) { ALOGE("Failed to writetoBuffer. DVR mq is not configured"); return -1; } if (buffer == nullptr) { ALOGE("Failed to writetoBuffer. Buffer can't be null"); return -1; } int64_t available = mDvrMQ->availableToRead(); size = min(size, available); if (mDvrMQ->read(buffer, size)) { mDvrMQEventFlag->wake(static_cast(DemuxQueueNotifyBits::DATA_CONSUMED)); } else { ALOGD("Failed to write FMQ"); return -1; } return size; } int64_t DvrClient::seekFile(int64_t pos) { if (mFd < 0) { ALOGE("Failed to seekFile. File is not configured"); return -1; } return lseek64(mFd, pos, SEEK_SET); } Result DvrClient::configure(DvrSettings settings) { if (mTunerDvr != nullptr) { Status s = mTunerDvr->configure(settings); Result res = ClientHelper::getServiceSpecificErrorCode(s); if (res != Result::SUCCESS) { return res; } AidlMQDesc aidlMqDesc; s = mTunerDvr->getQueueDesc(&aidlMqDesc); res = ClientHelper::getServiceSpecificErrorCode(s); if (res != Result::SUCCESS) { return res; } mDvrMQ = new (nothrow) AidlMQ(aidlMqDesc); EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrMQEventFlag); return res; } return Result::INVALID_STATE; } Result DvrClient::attachFilter(sp filterClient) { if (filterClient == nullptr) { return Result::INVALID_ARGUMENT; } if (mTunerDvr != nullptr) { Status s = mTunerDvr->attachFilter(filterClient->getAidlFilter()); return ClientHelper::getServiceSpecificErrorCode(s); } return Result::INVALID_STATE; } Result DvrClient::detachFilter(sp filterClient) { if (filterClient == nullptr) { return Result::INVALID_ARGUMENT; } if (mTunerDvr != nullptr) { Status s = mTunerDvr->detachFilter(filterClient->getAidlFilter()); return ClientHelper::getServiceSpecificErrorCode(s); } return Result::INVALID_STATE; } Result DvrClient::start() { if (mTunerDvr != nullptr) { Status s = mTunerDvr->start(); return ClientHelper::getServiceSpecificErrorCode(s); } return Result::INVALID_STATE; } Result DvrClient::stop() { if (mTunerDvr != nullptr) { Status s = mTunerDvr->stop(); return ClientHelper::getServiceSpecificErrorCode(s); } return Result::INVALID_STATE; } Result DvrClient::flush() { if (mTunerDvr != nullptr) { Status s = mTunerDvr->flush(); return ClientHelper::getServiceSpecificErrorCode(s); } return Result::INVALID_STATE; } Result DvrClient::close() { if (mDvrMQEventFlag != nullptr) { EventFlag::deleteEventFlag(&mDvrMQEventFlag); mDvrMQEventFlag = nullptr; } if (mDvrMQ != nullptr) { delete mDvrMQ; mDvrMQ = nullptr; } if (mTunerDvr != nullptr) { Status s = mTunerDvr->close(); mTunerDvr = nullptr; return ClientHelper::getServiceSpecificErrorCode(s); } return Result::INVALID_STATE; } Result DvrClient::setStatusCheckIntervalHint(int64_t durationInMs) { if (mTunerDvr == nullptr) { return Result::INVALID_STATE; } if (durationInMs < 0) { return Result::INVALID_ARGUMENT; } Status s = mTunerDvr->setStatusCheckIntervalHint(durationInMs); return ClientHelper::getServiceSpecificErrorCode(s); } /////////////// TunerDvrCallback /////////////////////// TunerDvrCallback::TunerDvrCallback(sp dvrClientCallback) : mDvrClientCallback(dvrClientCallback) {} Status TunerDvrCallback::onRecordStatus(RecordStatus status) { if (mDvrClientCallback != nullptr) { mDvrClientCallback->onRecordStatus(status); return Status::ok(); } return Status::fromServiceSpecificError(static_cast(Result::INVALID_STATE)); } Status TunerDvrCallback::onPlaybackStatus(PlaybackStatus status) { if (mDvrClientCallback != nullptr) { mDvrClientCallback->onPlaybackStatus(status); return Status::ok(); } return Status::fromServiceSpecificError(static_cast(Result::INVALID_STATE)); } } // namespace android