diff --git a/tv/tuner/1.0/default/Android.bp b/tv/tuner/1.0/default/Android.bp index 21c9f1b6c2..01bcbabc95 100644 --- a/tv/tuner/1.0/default/Android.bp +++ b/tv/tuner/1.0/default/Android.bp @@ -16,6 +16,8 @@ cc_defaults { shared_libs: [ "android.hardware.tv.tuner@1.0", "android.hidl.memory@1.0", + "libcutils", + "libfmq", "libhidlbase", "libhidlmemory", "libhidltransport", diff --git a/tv/tuner/1.0/default/Demux.cpp b/tv/tuner/1.0/default/Demux.cpp index 6f7c68bfb4..4016c5a0e9 100644 --- a/tv/tuner/1.0/default/Demux.cpp +++ b/tv/tuner/1.0/default/Demux.cpp @@ -26,12 +26,81 @@ namespace tuner { namespace V1_0 { namespace implementation { +#define WAIT_TIMEOUT 3000000000 + +const std::vector fakeDataInputBuffer{ + 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x1e, 0xdb, + 0x01, 0x40, 0x16, 0xec, 0x04, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x0f, 0x03, + 0xc5, 0x8b, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x68, 0xca, 0x8c, 0xb2, 0x00, 0x00, 0x01, 0x06, + 0x05, 0xff, 0xff, 0x70, 0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8, + 0x20, 0xd9, 0x23, 0xee, 0xef, 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63, 0x6f, 0x72, + 0x65, 0x20, 0x31, 0x34, 0x32, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32, 0x36, 0x34, 0x2f, 0x4d, + 0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, 0x43, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x63, + 0x20, 0x2d, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30, + 0x33, 0x2d, 0x32, 0x30, 0x31, 0x34, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c, 0x61, 0x6e, 0x2e, 0x6f, + 0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20, 0x2d, 0x20, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x20, 0x63, 0x61, 0x62, 0x61, 0x63, 0x3d, + 0x30, 0x20, 0x72, 0x65, 0x66, 0x3d, 0x32, 0x20, 0x64, 0x65, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x3d, 0x31, 0x3a, 0x30, 0x3a, 0x30, 0x20, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x65, 0x3d, + 0x30, 0x78, 0x31, 0x3a, 0x30, 0x78, 0x31, 0x31, 0x31, 0x20, 0x6d, 0x65, 0x3d, 0x68, 0x65, + 0x78, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x65, 0x3d, 0x37, 0x20, 0x70, 0x73, 0x79, 0x3d, 0x31, + 0x20, 0x70, 0x73, 0x79, 0x5f, 0x72, 0x64, 0x3d, 0x31, 0x2e, 0x30, 0x30, 0x3a, 0x30, 0x2e, + 0x30, 0x30, 0x20, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x3d, 0x31, 0x20, + 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x3d, 0x31, 0x36, 0x20, 0x63, 0x68, 0x72, + 0x6f, 0x6d, 0x61, 0x5f, 0x6d, 0x65, 0x3d, 0x31, 0x20, 0x74, 0x72, 0x65, 0x6c, 0x6c, 0x69, + 0x73, 0x3d, 0x31, 0x20, 0x38, 0x78, 0x38, 0x64, 0x63, 0x74, 0x3d, 0x30, 0x20, 0x63, 0x71, + 0x6d, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x61, 0x64, 0x7a, 0x6f, 0x6e, 0x65, 0x3d, 0x32, 0x31, + 0x2c, 0x31, 0x31, 0x20, 0x66, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x73, 0x6b, 0x69, 0x70, 0x3d, + 0x31, 0x20, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x5f, 0x71, 0x70, 0x5f, 0x6f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x3d, 0x2d, 0x32, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, + 0x36, 0x30, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x61, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x68, + 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x35, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x64, 0x5f, + 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x30, 0x20, 0x6e, 0x72, 0x3d, 0x30, 0x20, + 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x20, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x3d, 0x30, 0x20, 0x62, 0x6c, 0x75, 0x72, 0x61, 0x79, + 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x3d, 0x30, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x72, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x3d, 0x30, 0x20, + 0x62, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x3d, 0x30, 0x20, 0x77, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x70, 0x3d, 0x30, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x3d, 0x32, 0x35, 0x30, + 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x3d, 0x32, 0x35, 0x20, + 0x73, 0x63, 0x65, 0x6e, 0x65, +}; + Demux::Demux(uint32_t demuxId) { mDemuxId = demuxId; } Demux::~Demux() {} +bool Demux::createAndSaveMQ(uint32_t bufferSize, uint32_t filterId) { + ALOGV("%s", __FUNCTION__); + + // Create a synchronized FMQ that supports blocking read/write + std::unique_ptr tmpFilterMQ = + std::unique_ptr(new (std::nothrow) FilterMQ(bufferSize, true)); + if (!tmpFilterMQ->isValid()) { + ALOGW("Failed to create FMQ of filter with id: %d", filterId); + return false; + } + + mFilterMQs.resize(filterId + 1); + mFilterMQs[filterId] = std::move(tmpFilterMQ); + + EventFlag* mFilterEventFlag; + if (EventFlag::createEventFlag(mFilterMQs[filterId]->getEventFlagWord(), &mFilterEventFlag) != + OK) { + return false; + } + mFilterEventFlags.resize(filterId + 1); + mFilterEventFlags[filterId] = mFilterEventFlag; + mFilterWriteCount.resize(filterId + 1); + mFilterWriteCount[filterId] = 0; + mThreadRunning.resize(filterId + 1); + + return true; +} + Return Demux::setFrontendDataSource(uint32_t frontendId) { ALOGV("%s", __FUNCTION__); @@ -40,11 +109,292 @@ Return Demux::setFrontendDataSource(uint32_t frontendId) { return Result::SUCCESS; } +Return Demux::addFilter(DemuxFilterType type, uint32_t bufferSize, + const sp& cb, addFilter_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + uint32_t filterId = mLastUsedFilterId + 1; + mLastUsedFilterId += 1; + + if ((type != DemuxFilterType::PCR || type != DemuxFilterType::TS) && cb == nullptr) { + ALOGW("callback can't be null"); + _hidl_cb(Result::INVALID_ARGUMENT, filterId); + return Void(); + } + // Add callback + mDemuxCallbacks.resize(filterId + 1); + mDemuxCallbacks[filterId] = cb; + + // Mapping from the filter ID to the filter type + mFilterTypes.resize(filterId + 1); + mFilterTypes[filterId] = type; + + if (!createAndSaveMQ(bufferSize, filterId)) { + _hidl_cb(Result::UNKNOWN_ERROR, -1); + return Void(); + } + + _hidl_cb(Result::SUCCESS, filterId); + return Void(); +} + +Return Demux::getFilterQueueDesc(uint32_t filterId, getFilterQueueDesc_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + if (filterId < 0 || filterId > mLastUsedFilterId) { + ALOGW("No filter with id: %d exists", filterId); + _hidl_cb(Result::INVALID_ARGUMENT, FilterMQ::Descriptor()); + return Void(); + } + + _hidl_cb(Result::SUCCESS, *mFilterMQs[filterId]->getDesc()); + return Void(); +} + +Return Demux::configureFilter(uint32_t /* filterId */, + const DemuxFilterSettings& /* settings */) { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +Return Demux::startFilter(uint32_t filterId) { + ALOGV("%s", __FUNCTION__); + + if (filterId < 0 || filterId > mLastUsedFilterId) { + ALOGW("No filter with id: %d exists", filterId); + return Result::INVALID_ARGUMENT; + } + + DemuxFilterType filterType = mFilterTypes[filterId]; + Result result; + DemuxFilterEvent event{ + .filterId = filterId, + .filterType = filterType, + }; + + switch (filterType) { + case DemuxFilterType::SECTION: + result = startSectionFilterHandler(event); + break; + case DemuxFilterType::PES: + result = startPesFilterHandler(event); + break; + case DemuxFilterType::TS: + result = startTsFilterHandler(); + return Result::SUCCESS; + case DemuxFilterType::AUDIO: + case DemuxFilterType::VIDEO: + result = startMediaFilterHandler(event); + break; + case DemuxFilterType::RECORD: + result = startRecordFilterHandler(event); + break; + case DemuxFilterType::PCR: + result = startPcrFilterHandler(); + return Result::SUCCESS; + default: + return Result::UNKNOWN_ERROR; + } + + return result; +} + +Return Demux::stopFilter(uint32_t /* filterId */) { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +Return Demux::flushFilter(uint32_t /* filterId */) { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +Return Demux::removeFilter(uint32_t /* filterId */) { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +Return Demux::getAvSyncHwId(uint32_t /* filterId */, getAvSyncHwId_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + AvSyncHwId avSyncHwId = 0; + + _hidl_cb(Result::SUCCESS, avSyncHwId); + return Void(); +} + +Return Demux::getAvSyncTime(AvSyncHwId /* avSyncHwId */, getAvSyncTime_cb _hidl_cb) { + ALOGV("%s", __FUNCTION__); + + uint64_t avSyncTime = 0; + + _hidl_cb(Result::SUCCESS, avSyncTime); + return Void(); +} + Return Demux::close() { ALOGV("%s", __FUNCTION__); return Result::SUCCESS; - ; +} + +bool Demux::writeSectionsAndCreateEvent(DemuxFilterEvent& event, uint32_t sectionNum) { + event.events.resize(sectionNum); + for (int i = 0; i < sectionNum; i++) { + DemuxFilterSectionEvent secEvent; + secEvent = { + // temp dump meta data + .tableId = 0, + .version = 1, + .sectionNum = 1, + .dataLength = 530, + }; + event.events[i].section(secEvent); + if (!writeDataToFilterMQ(fakeDataInputBuffer, event.filterId)) { + return false; + } + } + return true; +} + +bool Demux::writeDataToFilterMQ(const std::vector& data, uint32_t filterId) { + std::lock_guard lock(mWriteLock); + if (mFilterMQs[filterId]->write(data.data(), data.size())) { + return true; + } + return false; +} + +Result Demux::startSectionFilterHandler(DemuxFilterEvent event) { + struct ThreadArgs* threadArgs = (struct ThreadArgs*)malloc(sizeof(struct ThreadArgs)); + threadArgs->user = this; + threadArgs->event = &event; + + pthread_create(&mThreadId, NULL, __threadLoop, (void*)threadArgs); + pthread_setname_np(mThreadId, "demux_filter_waiting_loop"); + + return Result::SUCCESS; +} + +Result Demux::startPesFilterHandler(DemuxFilterEvent& event) { + // TODO generate multiple events in one event callback + DemuxFilterPesEvent pesEvent; + pesEvent = { + // temp dump meta data + .streamId = 0, + .dataLength = 530, + }; + event.events.resize(1); + event.events[0].pes(pesEvent); + /*pthread_create(&mThreadId, NULL, __threadLoop, this); + pthread_setname_np(mThreadId, "demux_section_filter_waiting_loop");*/ + if (!writeDataToFilterMQ(fakeDataInputBuffer, event.filterId)) { + return Result::INVALID_STATE; + } + + if (mDemuxCallbacks[event.filterId] == nullptr) { + return Result::NOT_INITIALIZED; + } + + mDemuxCallbacks[event.filterId]->onFilterEvent(event); + return Result::SUCCESS; +} + +Result Demux::startTsFilterHandler() { + // TODO handle starting TS filter + return Result::SUCCESS; +} + +Result Demux::startMediaFilterHandler(DemuxFilterEvent& event) { + DemuxFilterMediaEvent mediaEvent; + mediaEvent = { + // temp dump meta data + .pts = 0, + .dataLength = 530, + .secureMemory = nullptr, + }; + event.events.resize(1); + event.events[0].media() = mediaEvent; + // TODO handle write FQM for media stream + return Result::SUCCESS; +} + +Result Demux::startRecordFilterHandler(DemuxFilterEvent& event) { + DemuxFilterRecordEvent recordEvent; + recordEvent = { + // temp dump meta data + .tpid = 0, + .packetNum = 0, + }; + recordEvent.indexMask.tsIndexMask() = 0x01; + event.events.resize(1); + event.events[0].ts() = recordEvent; + return Result::SUCCESS; +} + +Result Demux::startPcrFilterHandler() { + // TODO handle starting PCR filter + return Result::SUCCESS; +} + +void* Demux::__threadLoop(void* threadArg) { + Demux* const self = static_cast(((struct ThreadArgs*)threadArg)->user); + self->filterThreadLoop(((struct ThreadArgs*)threadArg)->event); + return 0; +} + +void Demux::filterThreadLoop(DemuxFilterEvent* event) { + uint32_t filterId = event->filterId; + ALOGD("[Demux] filter %d threadLoop start.", filterId); + mThreadRunning[filterId] = true; + + while (mThreadRunning[filterId]) { + uint32_t efState = 0; + // We do not wait for the last round of writen data to be read to finish the thread + // because the VTS can verify the reading itself. + for (int i = 0; i < SECTION_WRITE_COUNT; i++) { + DemuxFilterEvent filterEvent{ + .filterId = filterId, + .filterType = event->filterType, + }; + if (!writeSectionsAndCreateEvent(filterEvent, 2)) { + ALOGD("[Demux] filter %d fails to write into FMQ. Ending thread", filterId); + break; + } + mFilterWriteCount[filterId]++; + if (mDemuxCallbacks[filterId] == nullptr) { + ALOGD("[Demux] filter %d does not hava callback. Ending thread", filterId); + break; + } + // After successfully write, send a callback and wait for the read to be done + mDemuxCallbacks[filterId]->onFilterEvent(filterEvent); + // We do not wait for the last read to be done + // VTS can verify the read result itself. + if (i == SECTION_WRITE_COUNT - 1) { + ALOGD("[Demux] filter %d writing done. Ending thread", filterId); + break; + } + while (mThreadRunning[filterId]) { + status_t status = mFilterEventFlags[filterId]->wait( + static_cast(DemuxQueueNotifyBits::DATA_CONSUMED), &efState, + WAIT_TIMEOUT, true /* retry on spurious wake */); + if (status != OK) { + ALOGD("[Demux] wait for data consumed"); + continue; + } + break; + } + } + + mFilterWriteCount[filterId] = 0; + mThreadRunning[filterId] = false; + } + + ALOGD("[Demux] filter thread ended."); } } // namespace implementation diff --git a/tv/tuner/1.0/default/Demux.h b/tv/tuner/1.0/default/Demux.h index 52f39336bd..8b002669dd 100644 --- a/tv/tuner/1.0/default/Demux.h +++ b/tv/tuner/1.0/default/Demux.h @@ -18,6 +18,7 @@ #define ANDROID_HARDWARE_TV_TUNER_V1_0_DEMUX_H_ #include +#include using namespace std; @@ -28,9 +29,16 @@ namespace tuner { namespace V1_0 { namespace implementation { +using ::android::hardware::EventFlag; +using ::android::hardware::kSynchronizedReadWrite; +using ::android::hardware::MessageQueue; +using ::android::hardware::MQDescriptorSync; using ::android::hardware::tv::tuner::V1_0::IDemux; +using ::android::hardware::tv::tuner::V1_0::IDemuxCallback; using ::android::hardware::tv::tuner::V1_0::Result; +using FilterMQ = MessageQueue; + class Demux : public IDemux { public: Demux(uint32_t demuxId); @@ -39,10 +47,90 @@ class Demux : public IDemux { virtual Return close() override; + virtual Return addFilter(DemuxFilterType type, uint32_t bufferSize, + const sp& cb, addFilter_cb _hidl_cb) override; + + virtual Return getFilterQueueDesc(uint32_t filterId, + getFilterQueueDesc_cb _hidl_cb) override; + + virtual Return configureFilter(uint32_t filterId, + const DemuxFilterSettings& settings) override; + + virtual Return startFilter(uint32_t filterId) override; + + virtual Return stopFilter(uint32_t filterId) override; + + virtual Return flushFilter(uint32_t filterId) override; + + virtual Return removeFilter(uint32_t filterId) override; + + virtual Return getAvSyncHwId(uint32_t filterId, getAvSyncHwId_cb _hidl_cb) override; + + virtual Return getAvSyncTime(AvSyncHwId avSyncHwId, getAvSyncTime_cb _hidl_cb) override; + private: virtual ~Demux(); + /** + * To create a FilterMQ with the the next available Filter ID. + * Creating Event Flag at the same time. + * Add the successfully created/saved FilterMQ into the local list. + * + * Return false is any of the above processes fails. + */ + bool createAndSaveMQ(uint32_t bufferSize, uint32_t filterId); + void deleteEventFlag(); + bool writeDataToFilterMQ(const std::vector& data, uint32_t filterId); + Result startSectionFilterHandler(DemuxFilterEvent event); + Result startPesFilterHandler(DemuxFilterEvent& event); + Result startTsFilterHandler(); + Result startMediaFilterHandler(DemuxFilterEvent& event); + Result startRecordFilterHandler(DemuxFilterEvent& event); + Result startPcrFilterHandler(); + bool writeSectionsAndCreateEvent(DemuxFilterEvent& event, uint32_t sectionNum); + void filterThreadLoop(DemuxFilterEvent* event); + static void* __threadLoop(void* data); + uint32_t mDemuxId; uint32_t mSourceFrontendId; + /** + * Record the last used filer id. Initial value is -1. + * Filter Id starts with 0. + */ + uint32_t mLastUsedFilterId = -1; + /** + * A list of created FilterMQ ptrs. + * The array number is the filter ID. + */ + vector> mFilterMQs; + vector mFilterTypes; + vector mFilterEventFlags; + /** + * Demux callbacks used on filter events or IO buffer status + */ + vector> mDemuxCallbacks; + /** + * How many times a specific filter has written since started + */ + vector mFilterWriteCount; + pthread_t mThreadId = 0; + /** + * If a specific filter's writing loop is still running + */ + vector mThreadRunning; + /** + * Lock to protect writes to the FMQs + */ + std::mutex mWriteLock; + /** + * How many times a filter should write + * TODO make this dynamic/random/can take as a parameter + */ + const uint16_t SECTION_WRITE_COUNT = 10; + // A struct that passes the arguments to a newly created filter thread + struct ThreadArgs { + Demux* user; + DemuxFilterEvent* event; + }; }; } // namespace implementation diff --git a/tv/tuner/1.0/default/Descrambler.cpp b/tv/tuner/1.0/default/Descrambler.cpp index 1af1a2c995..085f2c8d54 100644 --- a/tv/tuner/1.0/default/Descrambler.cpp +++ b/tv/tuner/1.0/default/Descrambler.cpp @@ -44,6 +44,24 @@ Return Descrambler::setDemuxSource(uint32_t demuxId) { return Result::SUCCESS; } +Return Descrambler::setKeyToken(const hidl_vec& /* keyToken */) { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +Return Descrambler::addPid(uint16_t /* pid */) { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + +Return Descrambler::removePid(uint16_t /* pid */) { + ALOGV("%s", __FUNCTION__); + + return Result::SUCCESS; +} + Return Descrambler::close() { ALOGV("%s", __FUNCTION__); mDemuxSet = false; diff --git a/tv/tuner/1.0/default/Descrambler.h b/tv/tuner/1.0/default/Descrambler.h index 2ec8412a6d..436adcfc43 100644 --- a/tv/tuner/1.0/default/Descrambler.h +++ b/tv/tuner/1.0/default/Descrambler.h @@ -38,6 +38,12 @@ class Descrambler : public IDescrambler { virtual Return setDemuxSource(uint32_t demuxId) override; + virtual Return setKeyToken(const hidl_vec& keyToken) override; + + virtual Return addPid(uint16_t pid) override; + + virtual Return removePid(uint16_t pid) override; + virtual Return close() override; private: