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.
417 lines
12 KiB
417 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.
|
|
*/
|
|
|
|
#define LOG_TAG "DvrClient"
|
|
|
|
#include <android-base/logging.h>
|
|
#include <fmq/ConvertMQDescriptors.h>
|
|
#include <utils/Log.h>
|
|
|
|
#include "ClientHelper.h"
|
|
#include "DvrClient.h"
|
|
|
|
using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
|
|
using ::android::hardware::tv::tuner::V1_0::Result;
|
|
|
|
namespace android {
|
|
|
|
/////////////// DvrClient ///////////////////////
|
|
|
|
DvrClient::DvrClient(shared_ptr<ITunerDvr> tunerDvr) {
|
|
mTunerDvr = tunerDvr;
|
|
mFd = -1;
|
|
mDvrMQ = NULL;
|
|
mDvrMQEventFlag = NULL;
|
|
}
|
|
|
|
DvrClient::~DvrClient() {
|
|
mTunerDvr = NULL;
|
|
mDvr = NULL;
|
|
mFd = -1;
|
|
mDvrMQ = NULL;
|
|
mDvrMQEventFlag = NULL;
|
|
}
|
|
|
|
// TODO: remove after migration to Tuner Service is done.
|
|
void DvrClient::setHidlDvr(sp<IDvr> dvr) {
|
|
mDvr = dvr;
|
|
}
|
|
|
|
void DvrClient::setFd(int fd) {
|
|
mFd = fd;
|
|
}
|
|
|
|
long DvrClient::readFromFile(long size) {
|
|
if (mDvrMQ == NULL || mDvrMQEventFlag == NULL) {
|
|
ALOGE("Failed to readFromFile. DVR mq is not configured");
|
|
return -1;
|
|
}
|
|
if (mFd < 0) {
|
|
ALOGE("Failed to readFromFile. File is not configured");
|
|
return -1;
|
|
}
|
|
|
|
long available = mDvrMQ->availableToWrite();
|
|
long write = min(size, available);
|
|
|
|
AidlMQ::MemTransaction tx;
|
|
long ret = 0;
|
|
if (mDvrMQ->beginWrite(write, &tx)) {
|
|
auto first = tx.getFirstRegion();
|
|
auto data = first.getAddress();
|
|
long length = first.getLength();
|
|
long 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: %ld bytes to write, but %ld bytes written",
|
|
firstToWrite, ret);
|
|
} else if (firstToWrite < write) {
|
|
ALOGD("write second region: %ld bytes written, %ld bytes in total", ret, write);
|
|
auto second = tx.getSecondRegion();
|
|
data = second.getAddress();
|
|
length = second.getLength();
|
|
int secondToWrite = std::min(length, write - firstToWrite);
|
|
ret += read(mFd, data, secondToWrite);
|
|
}
|
|
ALOGD("file to MQ: %ld bytes need to be written, %ld 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<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
long DvrClient::readFromBuffer(int8_t* buffer, long size) {
|
|
if (mDvrMQ == NULL || mDvrMQEventFlag == NULL) {
|
|
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;
|
|
}
|
|
|
|
long available = mDvrMQ->availableToWrite();
|
|
size = min(size, available);
|
|
|
|
if (mDvrMQ->write(buffer, size)) {
|
|
mDvrMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
|
|
} else {
|
|
ALOGD("Failed to write FMQ");
|
|
return -1;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
long DvrClient::writeToFile(long size) {
|
|
if (mDvrMQ == NULL || mDvrMQEventFlag == NULL) {
|
|
ALOGE("Failed to writeToFile. DVR mq is not configured");
|
|
return -1;
|
|
}
|
|
if (mFd < 0) {
|
|
ALOGE("Failed to writeToFile. File is not configured");
|
|
return -1;
|
|
}
|
|
|
|
long available = mDvrMQ->availableToRead();
|
|
long toRead = min(size, available);
|
|
|
|
long ret = 0;
|
|
AidlMQ::MemTransaction tx;
|
|
if (mDvrMQ->beginRead(toRead, &tx)) {
|
|
auto first = tx.getFirstRegion();
|
|
auto data = first.getAddress();
|
|
long length = first.getLength();
|
|
long 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: %ld bytes read, but %ld bytes written", firstToRead, ret);
|
|
} else if (firstToRead < toRead) {
|
|
ALOGD("read second region: %ld bytes read, %ld bytes in total", ret, toRead);
|
|
auto second = tx.getSecondRegion();
|
|
data = second.getAddress();
|
|
length = second.getLength();
|
|
int secondToRead = toRead - firstToRead;
|
|
ret += write(mFd, data, secondToRead);
|
|
}
|
|
ALOGD("MQ to file: %ld bytes to be read, %ld 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<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
long DvrClient::writeToBuffer(int8_t* buffer, long size) {
|
|
if (mDvrMQ == NULL || mDvrMQEventFlag == NULL) {
|
|
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;
|
|
}
|
|
|
|
long available = mDvrMQ->availableToRead();
|
|
size = min(size, available);
|
|
|
|
if (mDvrMQ->read(buffer, size)) {
|
|
mDvrMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
|
|
} else {
|
|
ALOGD("Failed to write FMQ");
|
|
return -1;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
Result DvrClient::configure(DvrSettings settings) {
|
|
if (mTunerDvr != NULL) {
|
|
TunerDvrSettings dvrSettings = getAidlDvrSettingsFromHidl(settings);
|
|
Status s = mTunerDvr->configure(dvrSettings);
|
|
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;
|
|
}
|
|
|
|
if (mDvr != NULL) {
|
|
Result res = mDvr->configure(settings);
|
|
if (res == Result::SUCCESS) {
|
|
MQDescriptorSync<uint8_t> dvrMQDesc;
|
|
res = getQueueDesc(dvrMQDesc);
|
|
if (res == Result::SUCCESS) {
|
|
AidlMQDesc aidlMQDesc;
|
|
unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, SynchronizedReadWrite>(
|
|
dvrMQDesc, &aidlMQDesc);
|
|
mDvrMQ = new (nothrow) AidlMessageQueue(aidlMQDesc);
|
|
EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrMQEventFlag);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
return Result::INVALID_STATE;
|
|
}
|
|
|
|
Result DvrClient::attachFilter(sp<FilterClient> filterClient) {
|
|
if (mTunerDvr != NULL) {
|
|
Status s = mTunerDvr->attachFilter(filterClient->getAidlFilter());
|
|
return ClientHelper::getServiceSpecificErrorCode(s);
|
|
}
|
|
|
|
if (mDvr != NULL) {
|
|
sp<IFilter> hidlFilter = filterClient->getHalFilter();
|
|
if (hidlFilter == NULL) {
|
|
return Result::INVALID_ARGUMENT;
|
|
}
|
|
return mDvr->attachFilter(hidlFilter);
|
|
}
|
|
|
|
return Result::INVALID_STATE;
|
|
}
|
|
|
|
Result DvrClient::detachFilter(sp<FilterClient> filterClient) {
|
|
if (mTunerDvr != NULL) {
|
|
Status s = mTunerDvr->detachFilter(filterClient->getAidlFilter());
|
|
return ClientHelper::getServiceSpecificErrorCode(s);
|
|
}
|
|
|
|
if (mDvr != NULL) {
|
|
sp<IFilter> hidlFilter = filterClient->getHalFilter();
|
|
if (hidlFilter == NULL) {
|
|
return Result::INVALID_ARGUMENT;
|
|
}
|
|
return mDvr->detachFilter(hidlFilter);
|
|
}
|
|
|
|
return Result::INVALID_STATE;
|
|
}
|
|
|
|
Result DvrClient::start() {
|
|
if (mTunerDvr != NULL) {
|
|
Status s = mTunerDvr->start();
|
|
return ClientHelper::getServiceSpecificErrorCode(s);
|
|
}
|
|
|
|
if (mDvr != NULL) {
|
|
return mDvr->start();
|
|
}
|
|
|
|
return Result::INVALID_STATE;
|
|
}
|
|
|
|
Result DvrClient::stop() {
|
|
if (mTunerDvr != NULL) {
|
|
Status s = mTunerDvr->stop();
|
|
return ClientHelper::getServiceSpecificErrorCode(s);
|
|
}
|
|
|
|
if (mDvr != NULL) {
|
|
return mDvr->stop();
|
|
}
|
|
|
|
return Result::INVALID_STATE;
|
|
}
|
|
|
|
Result DvrClient::flush() {
|
|
if (mTunerDvr != NULL) {
|
|
Status s = mTunerDvr->flush();
|
|
return ClientHelper::getServiceSpecificErrorCode(s);
|
|
}
|
|
|
|
if (mDvr != NULL) {
|
|
return mDvr->flush();
|
|
}
|
|
|
|
return Result::INVALID_STATE;
|
|
}
|
|
|
|
Result DvrClient::close() {
|
|
if (mDvrMQEventFlag != NULL) {
|
|
EventFlag::deleteEventFlag(&mDvrMQEventFlag);
|
|
}
|
|
mDvrMQ = NULL;
|
|
|
|
if (mTunerDvr != NULL) {
|
|
Status s = mTunerDvr->close();
|
|
mTunerDvr = NULL;
|
|
return ClientHelper::getServiceSpecificErrorCode(s);
|
|
}
|
|
|
|
if (mDvr != NULL) {
|
|
Result res = mDvr->close();
|
|
mDvr = NULL;
|
|
return res;
|
|
}
|
|
|
|
return Result::INVALID_STATE;
|
|
}
|
|
|
|
/////////////// IDvrCallback ///////////////////////
|
|
|
|
HidlDvrCallback::HidlDvrCallback(sp<DvrClientCallback> dvrClientCallback)
|
|
: mDvrClientCallback(dvrClientCallback) {}
|
|
|
|
Return<void> HidlDvrCallback::onRecordStatus(const RecordStatus status) {
|
|
if (mDvrClientCallback != NULL) {
|
|
mDvrClientCallback->onRecordStatus(status);
|
|
}
|
|
return Void();
|
|
}
|
|
|
|
Return<void> HidlDvrCallback::onPlaybackStatus(const PlaybackStatus status) {
|
|
if (mDvrClientCallback != NULL) {
|
|
mDvrClientCallback->onPlaybackStatus(status);
|
|
}
|
|
return Void();
|
|
}
|
|
|
|
/////////////// TunerDvrCallback ///////////////////////
|
|
|
|
TunerDvrCallback::TunerDvrCallback(sp<DvrClientCallback> dvrClientCallback)
|
|
: mDvrClientCallback(dvrClientCallback) {}
|
|
|
|
Status TunerDvrCallback::onRecordStatus(int status) {
|
|
if (mDvrClientCallback != NULL) {
|
|
mDvrClientCallback->onRecordStatus(static_cast<RecordStatus>(status));
|
|
return Status::ok();
|
|
}
|
|
return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
|
|
}
|
|
|
|
Status TunerDvrCallback::onPlaybackStatus(int status) {
|
|
if (mDvrClientCallback != NULL) {
|
|
mDvrClientCallback->onPlaybackStatus(static_cast<PlaybackStatus>(status));
|
|
return Status::ok();
|
|
}
|
|
return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
|
|
}
|
|
|
|
/////////////// DvrClient Helper Methods ///////////////////////
|
|
|
|
Result DvrClient::getQueueDesc(MQDesc& dvrMQDesc) {
|
|
if (mDvr != NULL) {
|
|
Result res = Result::UNKNOWN_ERROR;
|
|
mDvr->getQueueDesc([&](Result r, const MQDesc& desc) {
|
|
dvrMQDesc = desc;
|
|
res = r;
|
|
});
|
|
return res;
|
|
}
|
|
|
|
return Result::INVALID_STATE;
|
|
}
|
|
|
|
TunerDvrSettings DvrClient::getAidlDvrSettingsFromHidl(DvrSettings settings) {
|
|
TunerDvrSettings s;
|
|
switch (settings.getDiscriminator()) {
|
|
case DvrSettings::hidl_discriminator::record: {
|
|
s.statusMask = static_cast<int>(settings.record().statusMask);
|
|
s.lowThreshold = static_cast<int>(settings.record().lowThreshold);
|
|
s.highThreshold = static_cast<int>(settings.record().highThreshold);
|
|
s.dataFormat = static_cast<int>(settings.record().dataFormat);
|
|
s.packetSize = static_cast<int>(settings.record().packetSize);
|
|
return s;
|
|
}
|
|
case DvrSettings::hidl_discriminator::playback: {
|
|
s.statusMask = static_cast<int>(settings.playback().statusMask);
|
|
s.lowThreshold = static_cast<int>(settings.playback().lowThreshold);
|
|
s.highThreshold = static_cast<int>(settings.playback().highThreshold);
|
|
s.dataFormat = static_cast<int>(settings.playback().dataFormat);
|
|
s.packetSize = static_cast<int>(settings.playback().packetSize);
|
|
return s;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
} // namespace android
|