diff --git a/media/jni/Android.bp b/media/jni/Android.bp index 79f6cbfa77292..25b1b4026265d 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -140,6 +140,7 @@ cc_library_shared { srcs: [ "android_media_tv_Tuner.cpp", "tuner/DemuxClient.cpp", + "tuner/DvrClient.cpp", "tuner/FilterClient.cpp", "tuner/FrontendClient.cpp", "tuner/TunerClient.cpp", diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 6fa94f1b7a7b5..602364e9e01af 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -261,70 +261,38 @@ sp Lnb::getILnb() { return mLnbSp; } -/////////////// DvrCallback /////////////////////// -Return DvrCallback::onRecordStatus(RecordStatus status) { - ALOGD("DvrCallback::onRecordStatus"); +/////////////// DvrClientCallbackImpl /////////////////////// +void DvrClientCallbackImpl::onRecordStatus(RecordStatus status) { + ALOGD("DvrClientCallbackImpl::onRecordStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod( - mDvr, + mDvrObj, gFields.onDvrRecordStatusID, (jint) status); - return Void(); } -Return DvrCallback::onPlaybackStatus(PlaybackStatus status) { - ALOGD("DvrCallback::onPlaybackStatus"); +void DvrClientCallbackImpl::onPlaybackStatus(PlaybackStatus status) { + ALOGD("DvrClientCallbackImpl::onPlaybackStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod( - mDvr, + mDvrObj, gFields.onDvrPlaybackStatusID, (jint) status); - return Void(); } -void DvrCallback::setDvr(const jobject dvr) { - ALOGD("DvrCallback::setDvr"); - JNIEnv *env = AndroidRuntime::getJNIEnv(); - mDvr = env->NewWeakGlobalRef(dvr); +void DvrClientCallbackImpl::setDvr(jweak dvrObj) { + ALOGD("DvrClientCallbackImpl::setDvr"); + mDvrObj = dvrObj; } -DvrCallback::~DvrCallback() { +DvrClientCallbackImpl::~DvrClientCallbackImpl() { JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (mDvr != NULL) { - env->DeleteWeakGlobalRef(mDvr); - mDvr = NULL; + if (mDvrObj != NULL) { + env->DeleteWeakGlobalRef(mDvrObj); + mDvrObj = NULL; } } -/////////////// Dvr /////////////////////// - -Dvr::Dvr(sp sp, jobject obj) : mDvrSp(sp), mDvrMQEventFlag(nullptr) { - JNIEnv *env = AndroidRuntime::getJNIEnv(); - mDvrObj = env->NewWeakGlobalRef(obj); -} - -Dvr::~Dvr() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); - env->DeleteWeakGlobalRef(mDvrObj); - mDvrObj = NULL; -} - -jint Dvr::close() { - Result r = mDvrSp->close(); - if (r == Result::SUCCESS) { - EventFlag::deleteEventFlag(&mDvrMQEventFlag); - } - return (jint) r; -} - -sp Dvr::getIDvr() { - return mDvrSp; -} - -MQ& Dvr::getDvrMQ() { - return *mDvrMQ; -} - /////////////// C2DataIdInfo /////////////////////// C2DataIdInfo::C2DataIdInfo(uint32_t index, uint64_t value) : C2Param(kParamSize, index) { @@ -1840,9 +1808,7 @@ jobject JTuner::openDescrambler() { jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) { if (mDemux == NULL || mDemuxClient == NULL) { - if (openDemux() != Result::SUCCESS) { - return NULL; - } + return NULL; } sp filterClient; @@ -1906,21 +1872,14 @@ jobject JTuner::openTimeFilter() { jobject JTuner::openDvr(DvrType type, jlong bufferSize) { ALOGD("JTuner::openDvr"); - if (mDemux == NULL) { - if (openDemux() != Result::SUCCESS) { - return NULL; - } + if (mDemuxClient == NULL) { + return NULL; } - sp iDvrSp; - sp callback = new DvrCallback(); - Result res; - mDemux->openDvr(type, (uint32_t) bufferSize, callback, - [&](Result r, const sp& dvr) { - res = r; - iDvrSp = dvr; - }); + sp dvrClient; + sp callback = new DvrClientCallbackImpl(); + dvrClient = mDemuxClient->openDvr(type, (int) bufferSize, callback); - if (res != Result::SUCCESS || iDvrSp == NULL) { + if (dvrClient == NULL) { return NULL; } @@ -1932,21 +1891,19 @@ jobject JTuner::openDvr(DvrType type, jlong bufferSize) { env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"), gFields.dvrRecorderInitID, mObject); - sp dvrSp = new Dvr(iDvrSp, dvrObj); - dvrSp->incStrong(dvrObj); - env->SetLongField(dvrObj, gFields.dvrRecorderContext, (jlong)dvrSp.get()); + dvrClient->incStrong(dvrObj); + env->SetLongField(dvrObj, gFields.dvrRecorderContext, (jlong)dvrClient.get()); } else { dvrObj = env->NewObject( env->FindClass("android/media/tv/tuner/dvr/DvrPlayback"), gFields.dvrPlaybackInitID, mObject); - sp dvrSp = new Dvr(iDvrSp, dvrObj); - dvrSp->incStrong(dvrObj); - env->SetLongField(dvrObj, gFields.dvrPlaybackContext, (jlong)dvrSp.get()); + dvrClient->incStrong(dvrObj); + env->SetLongField(dvrObj, gFields.dvrPlaybackContext, (jlong)dvrClient.get()); } - callback->setDvr(dvrObj); + callback->setDvr(env->NewWeakGlobalRef(dvrObj)); return dvrObj; } @@ -3304,12 +3261,12 @@ static DvrSettings getDvrSettings(JNIEnv *env, jobject settings, bool isRecorder return dvrSettings; } -static sp getDvr(JNIEnv *env, jobject dvr) { +static sp getDvrClient(JNIEnv *env, jobject dvr) { bool isRecorder = env->IsInstanceOf(dvr, env->FindClass("android/media/tv/tuner/dvr/DvrRecorder")); jfieldID fieldId = isRecorder ? gFields.dvrRecorderContext : gFields.dvrPlaybackContext; - return (Dvr *)env->GetLongField(dvr, fieldId); + return (DvrClient *)env->GetLongField(dvr, fieldId); } static void android_media_tv_Tuner_native_init(JNIEnv *env) { @@ -3910,33 +3867,6 @@ static Result configureIpFilterContextId( return filterClient->configureIpFilterContextId(cid); } -static jint copyData(JNIEnv *env, std::unique_ptr& mq, EventFlag* flag, jbyteArray buffer, - jlong offset, jlong size) { - ALOGD("copyData, size=%ld, offset=%ld", (long) size, (long) offset); - - jlong available = mq->availableToRead(); - ALOGD("copyData, available=%ld", (long) available); - size = std::min(size, available); - - jboolean isCopy; - jbyte *dst = env->GetByteArrayElements(buffer, &isCopy); - ALOGD("copyData, isCopy=%d", isCopy); - if (dst == nullptr) { - jniThrowRuntimeException(env, "Failed to GetByteArrayElements"); - return 0; - } - - if (mq->read(reinterpret_cast(dst) + offset, size)) { - env->ReleaseByteArrayElements(buffer, dst, 0); - flag->wake(static_cast(DemuxQueueNotifyBits::DATA_CONSUMED)); - } else { - jniThrowRuntimeException(env, "Failed to read FMQ"); - env->ReleaseByteArrayElements(buffer, dst, 0); - return 0; - } - return size; -} - static bool isAvFilterSettings(DemuxFilterSettings filterSettings) { return (filterSettings.getDiscriminator() == DemuxFilterSettings::hidl_discriminator::ts && filterSettings.ts().filterSettings.getDiscriminator() @@ -4068,7 +3998,7 @@ static jint android_media_tv_Tuner_read_filter_fmq( if (filterClient == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Failed to read filter FMQ: filter client not found"); - return 0; + return -1; } jboolean isCopy; @@ -4076,14 +4006,11 @@ static jint android_media_tv_Tuner_read_filter_fmq( ALOGD("copyData, isCopy=%d", isCopy); if (dst == nullptr) { jniThrowRuntimeException(env, "Failed to GetByteArrayElements"); - return 0; + return -1; } int realReadSize = filterClient->read(reinterpret_cast(dst) + offset, size); env->ReleaseByteArrayElements(buffer, dst, 0); - if (realReadSize < 0) { - return (jint) Result::UNKNOWN_ERROR; - } - return (jint) Result::SUCCESS; + return (jint) realReadSize; } static jint android_media_tv_Tuner_close_filter(JNIEnv *env, jobject filter) { @@ -4283,106 +4210,80 @@ static jint android_media_tv_Tuner_close_frontend(JNIEnv* env, jobject thiz, jin } static jint android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) { - sp dvrSp = getDvr(env, dvr); - if (dvrSp == NULL) { - return (jint) Result::NOT_INITIALIZED; - } sp filterClient = getFilterClient(env, filter); if (filterClient == NULL) { return (jint) Result::INVALID_ARGUMENT; } - sp iDvrSp = dvrSp->getIDvr(); - // TODO: use filter client once dvrClient is ready - sp iFilterSp = filterClient->getHalFilter(); - Result result = iDvrSp->attachFilter(iFilterSp); + sp dvrClient = getDvrClient(env, dvr); + if (dvrClient == NULL) { + return (jint) Result::NOT_INITIALIZED; + } + Result result = dvrClient->attachFilter(filterClient); return (jint) result; } static jint android_media_tv_Tuner_detach_filter(JNIEnv *env, jobject dvr, jobject filter) { - sp dvrSp = getDvr(env, dvr); - if (dvrSp == NULL) { - return (jint) Result::NOT_INITIALIZED; - } sp filterClient = getFilterClient(env, filter); if (filterClient == NULL) { return (jint) Result::INVALID_ARGUMENT; } - sp iDvrSp = dvrSp->getIDvr(); - // TODO: use filter client once dvrClient is ready - sp iFilterSp = filterClient->getHalFilter(); - Result result = iDvrSp->detachFilter(iFilterSp); + sp dvrClient = getDvrClient(env, dvr); + if (dvrClient == NULL) { + return (jint) Result::NOT_INITIALIZED; + } + Result result = dvrClient->detachFilter(filterClient); return (jint) result; } static jint android_media_tv_Tuner_configure_dvr(JNIEnv *env, jobject dvr, jobject settings) { - sp dvrSp = getDvr(env, dvr); - if (dvrSp == NULL) { - ALOGD("Failed to configure dvr: dvr not found"); + sp dvrClient = getDvrClient(env, dvr); + if (dvrClient == NULL) { + ALOGD("Failed to configure dvr: dvr client not found"); return (int)Result::NOT_INITIALIZED; } - sp iDvrSp = dvrSp->getIDvr(); bool isRecorder = env->IsInstanceOf(dvr, env->FindClass("android/media/tv/tuner/dvr/DvrRecorder")); - Result result = iDvrSp->configure(getDvrSettings(env, settings, isRecorder)); - if (result != Result::SUCCESS) { - return (jint) result; - } - MQDescriptorSync dvrMQDesc; - Result getQueueDescResult = Result::UNKNOWN_ERROR; - iDvrSp->getQueueDesc( - [&](Result r, const MQDescriptorSync& desc) { - dvrMQDesc = desc; - getQueueDescResult = r; - ALOGD("getDvrQueueDesc"); - }); - if (getQueueDescResult == Result::SUCCESS) { - dvrSp->mDvrMQ = std::make_unique(dvrMQDesc, true); - EventFlag::createEventFlag( - dvrSp->mDvrMQ->getEventFlagWord(), &(dvrSp->mDvrMQEventFlag)); - } - return (jint) getQueueDescResult; + Result result = dvrClient->configure(getDvrSettings(env, settings, isRecorder)); + return (jint) result; } static jint android_media_tv_Tuner_start_dvr(JNIEnv *env, jobject dvr) { - sp dvrSp = getDvr(env, dvr); - if (dvrSp == NULL) { - ALOGD("Failed to start dvr: dvr not found"); + sp dvrClient = getDvrClient(env, dvr); + if (dvrClient == NULL) { + ALOGD("Failed to start dvr: dvr client not found"); return (jint) Result::NOT_INITIALIZED; } - sp iDvrSp = dvrSp->getIDvr(); - Result result = iDvrSp->start(); + Result result = dvrClient->start(); return (jint) result; } static jint android_media_tv_Tuner_stop_dvr(JNIEnv *env, jobject dvr) { - sp dvrSp = getDvr(env, dvr); - if (dvrSp == NULL) { - ALOGD("Failed to stop dvr: dvr not found"); + sp dvrClient = getDvrClient(env, dvr); + if (dvrClient == NULL) { + ALOGD("Failed to stop dvr: dvr client not found"); return (jint) Result::NOT_INITIALIZED; } - sp iDvrSp = dvrSp->getIDvr(); - Result result = iDvrSp->stop(); + Result result = dvrClient->stop(); return (jint) result; } static jint android_media_tv_Tuner_flush_dvr(JNIEnv *env, jobject dvr) { - sp dvrSp = getDvr(env, dvr); - if (dvrSp == NULL) { - ALOGD("Failed to flush dvr: dvr not found"); + sp dvrClient = getDvrClient(env, dvr); + if (dvrClient == NULL) { + ALOGD("Failed to flush dvr: dvr client not found"); return (jint) Result::NOT_INITIALIZED; } - sp iDvrSp = dvrSp->getIDvr(); - Result result = iDvrSp->flush(); + Result result = dvrClient->flush(); return (jint) result; } static jint android_media_tv_Tuner_close_dvr(JNIEnv* env, jobject dvr) { - sp dvrSp = getDvr(env, dvr); - if (dvrSp == NULL) { - ALOGD("Failed to close dvr: dvr not found"); + sp dvrClient = getDvrClient(env, dvr); + if (dvrClient == NULL) { + ALOGD("Failed to close dvr: dvr client not found"); return (jint) Result::NOT_INITIALIZED; } - return dvrSp->close(); + return (jint) dvrClient->close(); } static sp getLnb(JNIEnv *env, jobject lnb) { @@ -4427,170 +4328,76 @@ static int android_media_tv_Tuner_close_lnb(JNIEnv* env, jobject lnb) { } static void android_media_tv_Tuner_dvr_set_fd(JNIEnv *env, jobject dvr, jint fd) { - sp dvrSp = getDvr(env, dvr); - if (dvrSp == NULL) { - ALOGD("Failed to set FD for dvr: dvr not found"); + sp dvrClient = getDvrClient(env, dvr); + if (dvrClient == NULL) { + ALOGD("Failed to set FD for dvr: dvr client not found"); + return; } - dvrSp->mFd = (int) fd; - ALOGD("set fd = %d", dvrSp->mFd); + dvrClient->setFd((int)fd); + ALOGD("set fd = %d", fd); } static jlong android_media_tv_Tuner_read_dvr(JNIEnv *env, jobject dvr, jlong size) { - sp dvrSp = getDvr(env, dvr); - if (dvrSp == NULL) { + sp dvrClient = getDvrClient(env, dvr); + if (dvrClient == NULL) { jniThrowException(env, "java/lang/IllegalStateException", - "Failed to read dvr: dvr not found"); - return 0; + "Failed to read dvr: dvr client not found"); + return -1; } - long available = dvrSp->mDvrMQ->availableToWrite(); - long write = std::min((long) size, available); - - MQ::MemTransaction tx; - long ret = 0; - if (dvrSp->mDvrMQ->beginWrite(write, &tx)) { - auto first = tx.getFirstRegion(); - auto data = first.getAddress(); - long length = first.getLength(); - long firstToWrite = std::min(length, write); - ret = read(dvrSp->mFd, data, firstToWrite); - - if (ret < 0) { - ALOGE("[DVR] Failed to read from FD: %s", strerror(errno)); - jniThrowRuntimeException(env, strerror(errno)); - return 0; - } - if (ret < firstToWrite) { - ALOGW("[DVR] file to MQ, first region: %ld bytes to write, but %ld bytes written", - firstToWrite, ret); - } else if (firstToWrite < write) { - ALOGD("[DVR] 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(dvrSp->mFd, data, secondToWrite); - } - ALOGD("[DVR] file to MQ: %ld bytes need to be written, %ld bytes written", write, ret); - if (!dvrSp->mDvrMQ->commitWrite(ret)) { - ALOGE("[DVR] Error: failed to commit write!"); - return 0; - } - - } else { - ALOGE("dvrMq.beginWrite failed"); - } - - if (ret > 0) { - dvrSp->mDvrMQEventFlag->wake(static_cast(DemuxQueueNotifyBits::DATA_READY)); - } - return (jlong) ret; + return (jlong) dvrClient->readFromFile(size); } static jlong android_media_tv_Tuner_read_dvr_from_array( JNIEnv* env, jobject dvr, jbyteArray buffer, jlong offset, jlong size) { - sp dvrSp = getDvr(env, dvr); - if (dvrSp == NULL) { - ALOGW("Failed to read dvr: dvr not found"); - return 0; + sp dvrClient = getDvrClient(env, dvr); + if (dvrClient == NULL) { + ALOGW("Failed to read dvr: dvr client not found"); + return -1; } - if (dvrSp->mDvrMQ == NULL) { - ALOGW("Failed to read dvr: dvr not configured"); - return 0; - } - - jlong available = dvrSp->mDvrMQ->availableToWrite(); - size = std::min(size, available); jboolean isCopy; jbyte *src = env->GetByteArrayElements(buffer, &isCopy); if (src == nullptr) { ALOGD("Failed to GetByteArrayElements"); - return 0; + return -1; } + long realSize = dvrClient->readFromBuffer(reinterpret_cast(src) + offset, size); + env->ReleaseByteArrayElements(buffer, src, 0); + return (jlong) realSize; - if (dvrSp->mDvrMQ->write(reinterpret_cast(src) + offset, size)) { - env->ReleaseByteArrayElements(buffer, src, 0); - dvrSp->mDvrMQEventFlag->wake(static_cast(DemuxQueueNotifyBits::DATA_READY)); - } else { - ALOGD("Failed to write FMQ"); - env->ReleaseByteArrayElements(buffer, src, 0); - return 0; - } - return size; } static jlong android_media_tv_Tuner_write_dvr(JNIEnv *env, jobject dvr, jlong size) { - sp dvrSp = getDvr(env, dvr); - if (dvrSp == NULL) { + sp dvrClient = getDvrClient(env, dvr); + if (dvrClient == NULL) { jniThrowException(env, "java/lang/IllegalStateException", - "Failed to write dvr: dvr not found"); - return 0; + "Failed to write dvr: dvr client not found"); + return -1; } - if (dvrSp->mDvrMQ == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", - "Failed to write dvr: dvr not configured"); - return 0; - } - - MQ& dvrMq = dvrSp->getDvrMQ(); - - long available = dvrMq.availableToRead(); - long toRead = std::min((long) size, available); - - long ret = 0; - MQ::MemTransaction tx; - if (dvrMq.beginRead(toRead, &tx)) { - auto first = tx.getFirstRegion(); - auto data = first.getAddress(); - long length = first.getLength(); - long firstToRead = std::min(length, toRead); - ret = write(dvrSp->mFd, data, firstToRead); - - if (ret < 0) { - ALOGE("[DVR] Failed to write to FD: %s", strerror(errno)); - jniThrowRuntimeException(env, strerror(errno)); - return 0; - } - if (ret < firstToRead) { - ALOGW("[DVR] MQ to file: %ld bytes read, but %ld bytes written", firstToRead, ret); - } else if (firstToRead < toRead) { - ALOGD("[DVR] 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(dvrSp->mFd, data, secondToRead); - } - ALOGD("[DVR] MQ to file: %ld bytes to be read, %ld bytes written", toRead, ret); - if (!dvrMq.commitRead(ret)) { - ALOGE("[DVR] Error: failed to commit read!"); - return 0; - } - - } else { - ALOGE("dvrMq.beginRead failed"); - } - if (ret > 0) { - dvrSp->mDvrMQEventFlag->wake(static_cast(DemuxQueueNotifyBits::DATA_CONSUMED)); - } - - return (jlong) ret; + return (jlong) dvrClient->writeToFile(size); } static jlong android_media_tv_Tuner_write_dvr_to_array( JNIEnv *env, jobject dvr, jbyteArray buffer, jlong offset, jlong size) { - sp dvrSp = getDvr(env, dvr); - if (dvrSp == NULL) { - ALOGW("Failed to write dvr: dvr not found"); - return 0; + sp dvrClient = getDvrClient(env, dvr); + if (dvrClient == NULL) { + ALOGW("Failed to read dvr: dvr client not found"); + return -1; } - if (dvrSp->mDvrMQ == NULL) { - ALOGW("Failed to write dvr: dvr not configured"); - return 0; + + jboolean isCopy; + jbyte *dst = env->GetByteArrayElements(buffer, &isCopy); + ALOGD("copyData, isCopy=%d", isCopy); + if (dst == nullptr) { + jniThrowRuntimeException(env, "Failed to GetByteArrayElements"); + return -1; } - return copyData(env, dvrSp->mDvrMQ, dvrSp->mDvrMQEventFlag, buffer, offset, size); + + long realSize = dvrClient->writeToBuffer(reinterpret_cast(dst) + offset, size); + env->ReleaseByteArrayElements(buffer, dst, 0); + return (jlong) realSize; } static sp getMediaEventSp(JNIEnv *env, jobject mediaEventObj) { diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index 4149228523778..9dc4ddfa1b617 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -105,29 +105,14 @@ struct Lnb : public RefBase { jweak mLnbObj; }; -struct DvrCallback : public IDvrCallback { - ~DvrCallback(); - virtual Return onRecordStatus(RecordStatus status); - virtual Return onPlaybackStatus(PlaybackStatus status); +struct DvrClientCallbackImpl : public DvrClientCallback { + ~DvrClientCallbackImpl(); + virtual void onRecordStatus(RecordStatus status); + virtual void onPlaybackStatus(PlaybackStatus status); - void setDvr(const jobject dvr); + void setDvr(jweak dvrObj); private: - jweak mDvr; -}; - -struct Dvr : public RefBase { - Dvr(sp sp, jweak obj); - ~Dvr(); - jint close(); - MQ& getDvrMQ(); - sp getIDvr(); - // TODO: remove after migrate to client lib - sp mDvrSp; jweak mDvrObj; - std::unique_ptr mDvrMQ; - EventFlag* mDvrMQEventFlag; - std::string mFilePath; - int mFd; }; struct MediaEvent : public RefBase { diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp index b237a242d03e3..59dfd704179ed 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 #include @@ -116,7 +116,21 @@ long DemuxClient::getAvSyncTime(int avSyncHwId) { return -1; } -//DvrClient openDvr(int dvbType, int bufferSize, DvrClientCallback cb); +sp DemuxClient::openDvr(DvrType dvbType, int bufferSize, sp cb) { + // TODO: pending aidl interface + + if (mDemux != NULL) { + sp callback = new HidlDvrCallback(cb); + sp hidlDvr = openHidlDvr(dvbType, bufferSize, callback); + if (hidlDvr != NULL) { + sp dvrClient = new DvrClient(); + dvrClient->setHidlDvr(hidlDvr); + return dvrClient; + } + } + + return NULL; +} Result DemuxClient::connectCiCam(int ciCamId) { // pending aidl interface @@ -173,4 +187,24 @@ sp DemuxClient::openHidlFilter(DemuxFilterType type, int bufferSize, return hidlFilter; } + +sp DemuxClient::openHidlDvr(DvrType dvrType, int bufferSize, + sp callback) { + if (mDemux == NULL) { + return NULL; + } + + sp hidlDvr; + Result res; + mDemux->openDvr(dvrType, bufferSize, callback, + [&](Result r, const sp& 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 a0671a53a7f7f..f11f2c6cc22b0 100644 --- a/media/jni/tuner/DemuxClient.h +++ b/media/jni/tuner/DemuxClient.h @@ -21,6 +21,8 @@ #include #include +#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 @@ public: /** * Open a DVR (Digital Video Record) client. */ - // TODO: handle DvrClient and callback - //DvrClient openDvr(int dvbType, int bufferSize, DvrClientCallback cb); + sp openDvr(DvrType dvbType, int bufferSize, sp cb); /** * Connect Conditional Access Modules (CAM) through Common Interface (CI). @@ -88,6 +90,7 @@ public: private: sp openHidlFilter(DemuxFilterType type, int bufferSize, sp cb); + sp openHidlDvr(DvrType type, int bufferSize, sp 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 0000000000000..dd08491d3a9fb --- /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 +#include + +#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 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(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(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(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(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 dvrMQDesc; + res = getQueueDesc(dvrMQDesc); + if (res == Result::SUCCESS) { + mDvrMQ = make_unique(dvrMQDesc, true); + EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrMQEventFlag); + } + } + return res; + } + + return Result::INVALID_STATE; +} + +Result DvrClient::attachFilter(sp filterClient) { + // pending aidl interface + + if (mDvr != NULL) { + sp hidlFilter = filterClient->getHalFilter(); + if (hidlFilter == NULL) { + return Result::INVALID_ARGUMENT; + } + return mDvr->attachFilter(hidlFilter); + } + + return Result::INVALID_STATE; +} + +Result DvrClient::detachFilter(sp filterClient) { + // pending aidl interface + + if (mDvr != NULL) { + sp 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) + : mDvrClientCallback(dvrClientCallback) {} + +Return HidlDvrCallback::onRecordStatus(const RecordStatus status) { + if (mDvrClientCallback != NULL) { + mDvrClientCallback->onRecordStatus(status); + } + return Void(); +} + +Return 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 0000000000000..2aba5e070cf9a --- /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 +#include +#include +#include +#include + +#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; +using MQDesc = MQDescriptorSync; + +namespace android { + +// TODO: pending aidl interface +/*class TunerDvrCallback : public BnTunerDvrCallback { + +public: + TunerDvrCallback(sp dvrClientCallback); + + Status onRecordStatus(int status); + Status onPlaybackStatus(int status); + +private: + sp mDvrClientCallback; +};*/ + +struct HidlDvrCallback : public IDvrCallback { + +public: + HidlDvrCallback(sp dvrClientCallback); + virtual Return onRecordStatus(const RecordStatus status); + virtual Return onPlaybackStatus(const PlaybackStatus status); + +private: + sp mDvrClientCallback; +}; + +struct DvrClient : public RefBase { + +public: + DvrClient(); + ~DvrClient(); + + // TODO: remove after migration to Tuner Service is done. + void setHidlDvr(sp 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); + + /** + * Detach one filter from the DVR's recording. + */ + Result detachFilter(sp 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 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 mDvr; + + unique_ptr 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 0000000000000..66844249cc052 --- /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 2c1735f5fc5af..bd18c707f18be 100644 --- a/media/jni/tuner/TunerClient.cpp +++ b/media/jni/tuner/TunerClient.cpp @@ -40,6 +40,8 @@ TunerClient::TunerClient() { // 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"); }