mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-03 12:07:58 +00:00
The previous impl keeps the demux and frontend mapping in the Tuner implementation even the instance has been closed. Clean up the mapping and the local vectors in the Tuner impl when Demux or Frontend is closed. Test: atest VtsHalTvTunerV1_0TargetTest Bug: 150952766 Change-Id: I2038e902ba7ab323c6dacc886ac6984ee7c475aa
396 lines
11 KiB
C++
396 lines
11 KiB
C++
/*
|
|
* 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 <utils/Log.h>
|
|
|
|
namespace android {
|
|
namespace hardware {
|
|
namespace tv {
|
|
namespace tuner {
|
|
namespace V1_0 {
|
|
namespace implementation {
|
|
|
|
#define WAIT_TIMEOUT 3000000000
|
|
|
|
Demux::Demux(uint32_t demuxId, sp<Tuner> tuner) {
|
|
mDemuxId = demuxId;
|
|
mTunerService = tuner;
|
|
}
|
|
|
|
Demux::~Demux() {}
|
|
|
|
Return<Result> 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<void> Demux::openFilter(const DemuxFilterType& type, uint32_t bufferSize,
|
|
const sp<IFilterCallback>& 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> 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<void> Demux::openTimeFilter(openTimeFilter_cb _hidl_cb) {
|
|
ALOGV("%s", __FUNCTION__);
|
|
|
|
mTimeFilter = new TimeFilter(this);
|
|
|
|
_hidl_cb(Result::SUCCESS, mTimeFilter);
|
|
return Void();
|
|
}
|
|
|
|
Return<void> Demux::getAvSyncHwId(const sp<IFilter>& 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<void> 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<Result> Demux::close() {
|
|
ALOGV("%s", __FUNCTION__);
|
|
|
|
set<uint32_t>::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<void> Demux::openDvr(DvrType type, uint32_t bufferSize, const sp<IDvrCallback>& 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<uint32_t>::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<Result> Demux::connectCiCam(uint32_t ciCamId) {
|
|
ALOGV("%s", __FUNCTION__);
|
|
|
|
mCiCamId = ciCamId;
|
|
|
|
return Result::SUCCESS;
|
|
}
|
|
|
|
Return<Result> 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<uint8_t> data) {
|
|
set<uint32_t>::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<uint8_t> data) {
|
|
set<uint32_t>::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<uint32_t>::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<uint32_t>::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<uint8_t> data) {
|
|
mFilters[filterId]->updateFilterOutput(data);
|
|
}
|
|
|
|
void Demux::updateMediaFilterOutput(uint16_t filterId, vector<uint8_t> 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<Demux*>(user);
|
|
self->frontendInputThreadLoop();
|
|
return 0;
|
|
}
|
|
|
|
void Demux::frontendInputThreadLoop() {
|
|
std::lock_guard<std::mutex> 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<uint32_t>(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<std::mutex> 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
|