/* * Copyright (C) 2019 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 "android.hardware.tv.tuner@1.0-Demux" #include "Demux.h" #include namespace android { namespace hardware { namespace tv { namespace tuner { namespace V1_0 { namespace implementation { #define WAIT_TIMEOUT 3000000000 Demux::Demux(uint32_t demuxId, sp tuner) { mDemuxId = demuxId; mTunerService = tuner; } Demux::~Demux() {} Return Demux::setFrontendDataSource(uint32_t frontendId) { ALOGV("%s", __FUNCTION__); if (mTunerService == nullptr) { return Result::NOT_INITIALIZED; } mFrontend = mTunerService->getFrontendById(frontendId); if (mFrontend == nullptr) { return Result::INVALID_STATE; } mTunerService->setFrontendAsDemuxSource(frontendId, mDemuxId); return Result::SUCCESS; } Return Demux::openFilter(const DemuxFilterType& type, uint32_t bufferSize, const sp& cb, openFilter_cb _hidl_cb) { ALOGV("%s", __FUNCTION__); uint32_t filterId; filterId = ++mLastUsedFilterId; if (cb == nullptr) { ALOGW("[Demux] callback can't be null"); _hidl_cb(Result::INVALID_ARGUMENT, new Filter()); return Void(); } sp filter = new Filter(type, filterId, bufferSize, cb, this); if (!filter->createFilterMQ()) { _hidl_cb(Result::UNKNOWN_ERROR, filter); return Void(); } mFilters[filterId] = filter; if (filter->isPcrFilter()) { mPcrFilterIds.insert(filterId); } bool result = true; if (!filter->isRecordFilter()) { // Only save non-record filters for now. Record filters are saved when the // IDvr.attacheFilter is called. mPlaybackFilterIds.insert(filterId); if (mDvrPlayback != nullptr) { result = mDvrPlayback->addPlaybackFilter(filterId, filter); } } _hidl_cb(result ? Result::SUCCESS : Result::INVALID_ARGUMENT, filter); return Void(); } Return Demux::openTimeFilter(openTimeFilter_cb _hidl_cb) { ALOGV("%s", __FUNCTION__); mTimeFilter = new TimeFilter(this); _hidl_cb(Result::SUCCESS, mTimeFilter); return Void(); } Return Demux::getAvSyncHwId(const sp& filter, getAvSyncHwId_cb _hidl_cb) { ALOGV("%s", __FUNCTION__); uint32_t avSyncHwId = -1; int id; Result status; filter->getId([&](Result result, uint32_t filterId) { id = filterId; status = result; }); if (status != Result::SUCCESS) { ALOGE("[Demux] Can't get filter Id."); _hidl_cb(Result::INVALID_STATE, avSyncHwId); return Void(); } if (!mFilters[id]->isMediaFilter()) { ALOGE("[Demux] Given filter is not a media filter."); _hidl_cb(Result::INVALID_ARGUMENT, avSyncHwId); return Void(); } if (!mPcrFilterIds.empty()) { // Return the lowest pcr filter id in the default implementation as the av sync id _hidl_cb(Result::SUCCESS, *mPcrFilterIds.begin()); return Void(); } ALOGE("[Demux] No PCR filter opened."); _hidl_cb(Result::INVALID_STATE, avSyncHwId); return Void(); } Return Demux::getAvSyncTime(AvSyncHwId avSyncHwId, getAvSyncTime_cb _hidl_cb) { ALOGV("%s", __FUNCTION__); uint64_t avSyncTime = -1; if (mPcrFilterIds.empty()) { _hidl_cb(Result::INVALID_STATE, avSyncTime); return Void(); } if (avSyncHwId != *mPcrFilterIds.begin()) { _hidl_cb(Result::INVALID_ARGUMENT, avSyncTime); return Void(); } _hidl_cb(Result::SUCCESS, avSyncTime); return Void(); } Return Demux::close() { ALOGV("%s", __FUNCTION__); set::iterator it; for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) { mDvrPlayback->removePlaybackFilter(*it); } mPlaybackFilterIds.clear(); mRecordFilterIds.clear(); mFilters.clear(); mLastUsedFilterId = -1; mTunerService->removeDemux(mDemuxId); return Result::SUCCESS; } Return Demux::openDvr(DvrType type, uint32_t bufferSize, const sp& cb, openDvr_cb _hidl_cb) { ALOGV("%s", __FUNCTION__); if (cb == nullptr) { ALOGW("[Demux] DVR callback can't be null"); _hidl_cb(Result::INVALID_ARGUMENT, new Dvr()); return Void(); } set::iterator it; switch (type) { case DvrType::PLAYBACK: mDvrPlayback = new Dvr(type, bufferSize, cb, this); if (!mDvrPlayback->createDvrMQ()) { _hidl_cb(Result::UNKNOWN_ERROR, mDvrPlayback); return Void(); } for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) { if (!mDvrPlayback->addPlaybackFilter(*it, mFilters[*it])) { ALOGE("[Demux] Can't get filter info for DVR playback"); _hidl_cb(Result::UNKNOWN_ERROR, mDvrPlayback); return Void(); } } _hidl_cb(Result::SUCCESS, mDvrPlayback); return Void(); case DvrType::RECORD: mDvrRecord = new Dvr(type, bufferSize, cb, this); if (!mDvrRecord->createDvrMQ()) { _hidl_cb(Result::UNKNOWN_ERROR, mDvrRecord); return Void(); } _hidl_cb(Result::SUCCESS, mDvrRecord); return Void(); default: _hidl_cb(Result::INVALID_ARGUMENT, nullptr); return Void(); } } Return Demux::connectCiCam(uint32_t ciCamId) { ALOGV("%s", __FUNCTION__); mCiCamId = ciCamId; return Result::SUCCESS; } Return Demux::disconnectCiCam() { ALOGV("%s", __FUNCTION__); return Result::SUCCESS; } Result Demux::removeFilter(uint32_t filterId) { ALOGV("%s", __FUNCTION__); if (mDvrPlayback != nullptr) { mDvrPlayback->removePlaybackFilter(filterId); } mPlaybackFilterIds.erase(filterId); mRecordFilterIds.erase(filterId); mFilters.erase(filterId); return Result::SUCCESS; } void Demux::startBroadcastTsFilter(vector data) { set::iterator it; uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff)); if (DEBUG_DEMUX) { ALOGW("[Demux] start ts filter pid: %d", pid); } for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) { if (pid == mFilters[*it]->getTpid()) { mFilters[*it]->updateFilterOutput(data); } } } void Demux::sendFrontendInputToRecord(vector data) { set::iterator it; if (DEBUG_DEMUX) { ALOGW("[Demux] update record filter output"); } for (it = mRecordFilterIds.begin(); it != mRecordFilterIds.end(); it++) { mFilters[*it]->updateRecordOutput(data); } } bool Demux::startBroadcastFilterDispatcher() { set::iterator it; // Handle the output data per filter type for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) { if (mFilters[*it]->startFilterHandler() != Result::SUCCESS) { return false; } } return true; } bool Demux::startRecordFilterDispatcher() { set::iterator it; for (it = mRecordFilterIds.begin(); it != mRecordFilterIds.end(); it++) { if (mFilters[*it]->startRecordFilterHandler() != Result::SUCCESS) { return false; } } return true; } Result Demux::startFilterHandler(uint32_t filterId) { return mFilters[filterId]->startFilterHandler(); } void Demux::updateFilterOutput(uint16_t filterId, vector data) { mFilters[filterId]->updateFilterOutput(data); } void Demux::updateMediaFilterOutput(uint16_t filterId, vector data, uint64_t pts) { updateFilterOutput(filterId, data); mFilters[filterId]->updatePts(pts); } uint16_t Demux::getFilterTpid(uint32_t filterId) { return mFilters[filterId]->getTpid(); } void Demux::startFrontendInputLoop() { pthread_create(&mFrontendInputThread, NULL, __threadLoopFrontend, this); pthread_setname_np(mFrontendInputThread, "frontend_input_thread"); } void* Demux::__threadLoopFrontend(void* user) { Demux* const self = static_cast(user); self->frontendInputThreadLoop(); return 0; } void Demux::frontendInputThreadLoop() { std::lock_guard lock(mFrontendInputThreadLock); mFrontendInputThreadRunning = true; if (!mDvrPlayback) { ALOGW("[Demux] No software Frontend input configured. Ending Frontend thread loop."); mFrontendInputThreadRunning = false; return; } while (mFrontendInputThreadRunning) { uint32_t efState = 0; status_t status = mDvrPlayback->getDvrEventFlag()->wait( static_cast(DemuxQueueNotifyBits::DATA_READY), &efState, WAIT_TIMEOUT, true /* retry on spurious wake */); if (status != OK) { ALOGD("[Demux] wait for data ready on the playback FMQ"); continue; } if (mDvrPlayback->getSettings().playback().dataFormat == DataFormat::ES) { if (!mDvrPlayback->processEsDataOnPlayback(true /*isVirtualFrontend*/, mIsRecording)) { ALOGE("[Demux] playback es data failed to be filtered. Ending thread"); break; } } // Our current implementation filter the data and write it into the filter FMQ immediately // after the DATA_READY from the VTS/framework if (!mDvrPlayback->readPlaybackFMQ(true /*isVirtualFrontend*/, mIsRecording) || !mDvrPlayback->startFilterDispatcher(true /*isVirtualFrontend*/, mIsRecording)) { ALOGE("[Demux] playback data failed to be filtered. Ending thread"); break; } } mFrontendInputThreadRunning = false; ALOGW("[Demux] Frontend Input thread end."); } void Demux::stopFrontendInput() { ALOGD("[Demux] stop frontend on demux"); mKeepFetchingDataFromFrontend = false; mFrontendInputThreadRunning = false; std::lock_guard lock(mFrontendInputThreadLock); } void Demux::setIsRecording(bool isRecording) { mIsRecording = isRecording; } bool Demux::attachRecordFilter(int filterId) { if (mFilters[filterId] == nullptr || mDvrRecord == nullptr || !mFilters[filterId]->isRecordFilter()) { return false; } mRecordFilterIds.insert(filterId); mFilters[filterId]->attachFilterToRecord(mDvrRecord); return true; } bool Demux::detachRecordFilter(int filterId) { if (mFilters[filterId] == nullptr || mDvrRecord == nullptr) { return false; } mRecordFilterIds.erase(filterId); mFilters[filterId]->detachFilterFromRecord(); return true; } } // namespace implementation } // namespace V1_0 } // namespace tuner } // namespace tv } // namespace hardware } // namespace android