Add DvrClient and DvrClientCallback
Test: make libmedia_tv_tuner
Bug: 174095851
Change-Id: I0614a8ca7ca8d177da3f8ad07dbe70c3f57d6f1e
diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp
index b237a24..59dfd70 100644
--- a/media/jni/tuner/DemuxClient.cpp
+++ b/media/jni/tuner/DemuxClient.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "FrontendClient"
+#define LOG_TAG "DemuxClient"
#include <android-base/logging.h>
#include <utils/Log.h>
@@ -116,7 +116,21 @@
return -1;
}
-//DvrClient openDvr(int dvbType, int bufferSize, DvrClientCallback cb);
+sp<DvrClient> DemuxClient::openDvr(DvrType dvbType, int bufferSize, sp<DvrClientCallback> cb) {
+ // TODO: pending aidl interface
+
+ if (mDemux != NULL) {
+ sp<HidlDvrCallback> callback = new HidlDvrCallback(cb);
+ sp<IDvr> hidlDvr = openHidlDvr(dvbType, bufferSize, callback);
+ if (hidlDvr != NULL) {
+ sp<DvrClient> dvrClient = new DvrClient();
+ dvrClient->setHidlDvr(hidlDvr);
+ return dvrClient;
+ }
+ }
+
+ return NULL;
+}
Result DemuxClient::connectCiCam(int ciCamId) {
// pending aidl interface
@@ -173,4 +187,24 @@
return hidlFilter;
}
+
+sp<IDvr> DemuxClient::openHidlDvr(DvrType dvrType, int bufferSize,
+ sp<HidlDvrCallback> callback) {
+ if (mDemux == NULL) {
+ return NULL;
+ }
+
+ sp<IDvr> hidlDvr;
+ Result res;
+ mDemux->openDvr(dvrType, bufferSize, callback,
+ [&](Result r, const sp<IDvr>& dvr) {
+ hidlDvr = dvr;
+ res = r;
+ });
+ if (res != Result::SUCCESS || hidlDvr == NULL) {
+ return NULL;
+ }
+
+ return hidlDvr;
+}
} // namespace android
diff --git a/media/jni/tuner/DemuxClient.h b/media/jni/tuner/DemuxClient.h
index a0671a5..f11f2c6 100644
--- a/media/jni/tuner/DemuxClient.h
+++ b/media/jni/tuner/DemuxClient.h
@@ -21,6 +21,8 @@
#include <android/hardware/tv/tuner/1.0/IDemux.h>
#include <android/hardware/tv/tuner/1.1/types.h>
+#include "DvrClient.h"
+#include "DvrClientCallback.h"
#include "FilterClient.h"
#include "FilterClientCallback.h"
#include "FrontendClient.h"
@@ -28,6 +30,7 @@
//using ::aidl::android::media::tv::tuner::ITunerDemux;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
+using ::android::hardware::tv::tuner::V1_0::DvrType;
using ::android::hardware::tv::tuner::V1_0::IDemux;
using namespace std;
@@ -68,8 +71,7 @@
/**
* Open a DVR (Digital Video Record) client.
*/
- // TODO: handle DvrClient and callback
- //DvrClient openDvr(int dvbType, int bufferSize, DvrClientCallback cb);
+ sp<DvrClient> openDvr(DvrType dvbType, int bufferSize, sp<DvrClientCallback> cb);
/**
* Connect Conditional Access Modules (CAM) through Common Interface (CI).
@@ -88,6 +90,7 @@
private:
sp<IFilter> openHidlFilter(DemuxFilterType type, int bufferSize, sp<HidlFilterCallback> cb);
+ sp<IDvr> openHidlDvr(DvrType type, int bufferSize, sp<HidlDvrCallback> cb);
/**
* An AIDL Tuner Demux Singleton assigned at the first time the Tuner Client
diff --git a/media/jni/tuner/DvrClient.cpp b/media/jni/tuner/DvrClient.cpp
new file mode 100644
index 0000000..dd08491
--- /dev/null
+++ b/media/jni/tuner/DvrClient.cpp
@@ -0,0 +1,329 @@
+/*
+ * 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 <utils/Log.h>
+
+#include "DvrClient.h"
+
+using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+namespace android {
+
+/////////////// DvrClient ///////////////////////
+
+// TODO: pending aidl interface
+DvrClient::DvrClient() {
+ //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);
+
+ MQ::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(uint8_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;
+ MQ::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(uint8_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) {
+ // pending aidl interface
+
+ if (mDvr != NULL) {
+ Result res = mDvr->configure(settings);
+ if (res == Result::SUCCESS) {
+ MQDescriptorSync<uint8_t> dvrMQDesc;
+ res = getQueueDesc(dvrMQDesc);
+ if (res == Result::SUCCESS) {
+ mDvrMQ = make_unique<MQ>(dvrMQDesc, true);
+ EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrMQEventFlag);
+ }
+ }
+ return res;
+ }
+
+ return Result::INVALID_STATE;
+}
+
+Result DvrClient::attachFilter(sp<FilterClient> filterClient) {
+ // pending aidl interface
+
+ 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) {
+ // pending aidl interface
+
+ 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() {
+ // pending aidl interface
+
+ if (mDvr != NULL) {
+ return mDvr->start();
+ }
+
+ return Result::INVALID_STATE;
+}
+
+Result DvrClient::stop() {
+ // pending aidl interface
+
+ if (mDvr != NULL) {
+ return mDvr->stop();
+ }
+
+ return Result::INVALID_STATE;
+}
+
+Result DvrClient::flush() {
+ // pending aidl interface
+
+ if (mDvr != NULL) {
+ return mDvr->flush();
+ }
+
+ return Result::INVALID_STATE;
+}
+
+Result DvrClient::close() {
+ // pending aidl interface
+
+ if (mDvr != NULL) {
+ Result res = mDvr->close();
+ if (res == Result::SUCCESS) {
+ 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();
+}
+
+/////////////// DvrClient Helper Methods ///////////////////////
+
+Result DvrClient::getQueueDesc(MQDesc& dvrMQDesc) {
+ // pending aidl interface
+
+ 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;
+}
+} // namespace android
diff --git a/media/jni/tuner/DvrClient.h b/media/jni/tuner/DvrClient.h
new file mode 100644
index 0000000..2aba5e0
--- /dev/null
+++ b/media/jni/tuner/DvrClient.h
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_MEDIA_TV_DVR_CLIENT_H_
+#define _ANDROID_MEDIA_TV_DVR_CLIENT_H_
+
+//#include <aidl/android/media/tv/tuner/ITunerDvr.h>
+#include <android/hardware/tv/tuner/1.0/IDvr.h>
+#include <android/hardware/tv/tuner/1.0/IDvrCallback.h>
+#include <android/hardware/tv/tuner/1.1/types.h>
+#include <fmq/MessageQueue.h>
+
+#include "DvrClientCallback.h"
+#include "FilterClient.h"
+
+//using ::aidl::android::media::tv::tuner::ITunerDvr;
+
+using ::android::hardware::EventFlag;
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::tv::tuner::V1_0::DvrSettings;
+using ::android::hardware::tv::tuner::V1_0::IDvr;
+using ::android::hardware::tv::tuner::V1_0::IDvrCallback;
+
+using namespace std;
+
+using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using MQDesc = MQDescriptorSync<uint8_t>;
+
+namespace android {
+
+// TODO: pending aidl interface
+/*class TunerDvrCallback : public BnTunerDvrCallback {
+
+public:
+ TunerDvrCallback(sp<DvrClientCallback> dvrClientCallback);
+
+ Status onRecordStatus(int status);
+ Status onPlaybackStatus(int status);
+
+private:
+ sp<DvrClientCallback> mDvrClientCallback;
+};*/
+
+struct HidlDvrCallback : public IDvrCallback {
+
+public:
+ HidlDvrCallback(sp<DvrClientCallback> dvrClientCallback);
+ virtual Return<void> onRecordStatus(const RecordStatus status);
+ virtual Return<void> onPlaybackStatus(const PlaybackStatus status);
+
+private:
+ sp<DvrClientCallback> mDvrClientCallback;
+};
+
+struct DvrClient : public RefBase {
+
+public:
+ DvrClient();
+ ~DvrClient();
+
+ // TODO: remove after migration to Tuner Service is done.
+ void setHidlDvr(sp<IDvr> dvr);
+
+ /**
+ * Set the DVR file descriptor.
+ */
+ void setFd(int fd);
+
+ /**
+ * Read data from file with given size. Return the actual read size.
+ */
+ long readFromFile(long size);
+
+ /**
+ * Read data from the given buffer with given size. Return the actual read size.
+ */
+ long readFromBuffer(uint8_t* buffer, long size);
+
+ /**
+ * Write data to file with given size. Return the actual write size.
+ */
+ long writeToFile(long size);
+
+ /**
+ * Write data to the given buffer with given size. Return the actual write size.
+ */
+ long writeToBuffer(uint8_t* buffer, long size);
+
+ /**
+ * Configure the DVR.
+ */
+ Result configure(DvrSettings settings);
+
+ /**
+ * Attach one filter to DVR interface for recording.
+ */
+ Result attachFilter(sp<FilterClient> filterClient);
+
+ /**
+ * Detach one filter from the DVR's recording.
+ */
+ Result detachFilter(sp<FilterClient> filterClient);
+
+ /**
+ * Start DVR.
+ */
+ Result start();
+
+ /**
+ * Stop DVR.
+ */
+ Result stop();
+
+ /**
+ * Flush DVR data.
+ */
+ Result flush();
+
+ /**
+ * close the DVR instance to release resource for DVR.
+ */
+ Result close();
+
+private:
+ Result getQueueDesc(MQDesc& dvrMQDesc);
+
+ /**
+ * An AIDL Tuner Dvr Singleton assigned at the first time the Tuner Client
+ * opens a dvr. Default null when dvr is not opened.
+ */
+ // TODO: pending on aidl interface
+ //shared_ptr<ITunerDvr> mTunerDvr;
+
+ /**
+ * A Dvr HAL interface that is ready before migrating to the TunerDvr.
+ * This is a temprary interface before Tuner Framework migrates to use TunerService.
+ * Default null when the HAL service does not exist.
+ */
+ sp<IDvr> mDvr;
+
+ unique_ptr<MQ> mDvrMQ;
+ EventFlag* mDvrMQEventFlag;
+ string mFilePath;
+ int mFd;
+};
+} // namespace android
+
+#endif // _ANDROID_MEDIA_TV_DVR_CLIENT_H_
diff --git a/media/jni/tuner/DvrClientCallback.h b/media/jni/tuner/DvrClientCallback.h
new file mode 100644
index 0000000..6684424
--- /dev/null
+++ b/media/jni/tuner/DvrClientCallback.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_MEDIA_TV_DVR_CLIENT_CALLBACK_H_
+#define _ANDROID_MEDIA_TV_DVR_CLIENT_CALLBACK_H_
+
+using ::android::hardware::tv::tuner::V1_0::PlaybackStatus;
+using ::android::hardware::tv::tuner::V1_0::RecordStatus;
+
+using namespace std;
+
+namespace android {
+
+struct DvrClientCallback : public RefBase {
+ virtual void onRecordStatus(const RecordStatus status);
+ virtual void onPlaybackStatus(const PlaybackStatus status);
+};
+} // namespace android
+
+#endif // _ANDROID_MEDIA_TV_DVR_CLIENT_CALLBACK_H_
\ No newline at end of file
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index 2c1735f..bd18c707 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -40,6 +40,8 @@
// Connect with Tuner Service.
::ndk::SpAIBinder binder(AServiceManager_getService("media.tuner"));
mTunerService = ITunerService::fromBinder(binder);
+ // TODO: Remove after JNI migration is done.
+ mTunerService = NULL;
if (mTunerService == NULL) {
ALOGE("Failed to get tuner service");
}