mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 11:36:00 +00:00
Merge changes from topic "Tuner_1.1"
* changes: Add VTS of Tuner HAL 1.1 Add default implementation of Tuner HAL 1.1 Enable Tuner HAL 1.1 interface
This commit is contained in:
@@ -482,7 +482,7 @@
|
||||
</hal>
|
||||
<hal format="hidl" optional="true">
|
||||
<name>android.hardware.tv.tuner</name>
|
||||
<version>1.0</version>
|
||||
<version>1.0-1</version>
|
||||
<interface>
|
||||
<name>ITuner</name>
|
||||
<instance>default</instance>
|
||||
|
||||
16
tv/tuner/1.1/Android.bp
Normal file
16
tv/tuner/1.1/Android.bp
Normal file
@@ -0,0 +1,16 @@
|
||||
// This file is autogenerated by hidl-gen -Landroidbp.
|
||||
|
||||
hidl_interface {
|
||||
name: "android.hardware.tv.tuner@1.1",
|
||||
root: "android.hardware",
|
||||
srcs: [
|
||||
"IFilter.hal",
|
||||
"ITuner.hal",
|
||||
],
|
||||
interfaces: [
|
||||
"android.hidl.base@1.0",
|
||||
"android.hidl.safe_union@1.0",
|
||||
"android.hardware.tv.tuner@1.0",
|
||||
],
|
||||
gen_java: false,
|
||||
}
|
||||
43
tv/tuner/1.1/IFilter.hal
Normal file
43
tv/tuner/1.1/IFilter.hal
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package android.hardware.tv.tuner@1.1;
|
||||
|
||||
import @1.0::IFilter;
|
||||
import @1.0::Result;
|
||||
|
||||
/**
|
||||
* The Filter is used to filter wanted data according to the filter's
|
||||
* configuration.
|
||||
*
|
||||
* To access the v1.1 IFilter APIs, the implementation can cast the IFilter
|
||||
* interface returned from the @1.0::IDemux openFilter into a v1.1 IFiler
|
||||
* using V1_1::IFilter::castFrom(V1_0::IFilter).
|
||||
*/
|
||||
interface IFilter extends @1.0::IFilter {
|
||||
/**
|
||||
* Get the 64-bit filter Id. This id is 32-bit in Tuner HAL 1.0.
|
||||
*
|
||||
* It is used by the client to ask the hardware resource id for the filter.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
* @return filterId the hardware resource Id for the filter.
|
||||
*/
|
||||
getId64Bit() generates (Result result, uint64_t filterId);
|
||||
};
|
||||
26
tv/tuner/1.1/ITuner.hal
Normal file
26
tv/tuner/1.1/ITuner.hal
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package android.hardware.tv.tuner@1.1;
|
||||
|
||||
import @1.0::ITuner;
|
||||
import @1.0::Result;
|
||||
|
||||
/**
|
||||
* Top level interface to manage Frontend, Demux and Decrambler hardware
|
||||
* resources which are needed for Android TV.
|
||||
*/
|
||||
interface ITuner extends @1.0::ITuner {};
|
||||
7
tv/tuner/1.1/TEST_MAPPING
Normal file
7
tv/tuner/1.1/TEST_MAPPING
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"presubmit": [
|
||||
{
|
||||
"name": "VtsHalTvTunerV1_1TargetTest"
|
||||
}
|
||||
]
|
||||
}
|
||||
52
tv/tuner/1.1/default/Android.bp
Normal file
52
tv/tuner/1.1/default/Android.bp
Normal file
@@ -0,0 +1,52 @@
|
||||
cc_defaults {
|
||||
name: "tuner_service_defaults@1.1",
|
||||
defaults: ["hidl_defaults"],
|
||||
vendor: true,
|
||||
relative_install_path: "hw",
|
||||
srcs: [
|
||||
"Filter.cpp",
|
||||
"Frontend.cpp",
|
||||
"Descrambler.cpp",
|
||||
"Demux.cpp",
|
||||
"Dvr.cpp",
|
||||
"TimeFilter.cpp",
|
||||
"Tuner.cpp",
|
||||
"Lnb.cpp",
|
||||
"service.cpp",
|
||||
],
|
||||
|
||||
compile_multilib: "first",
|
||||
|
||||
shared_libs: [
|
||||
"android.hardware.tv.tuner@1.0",
|
||||
"android.hardware.tv.tuner@1.1",
|
||||
"android.hidl.memory@1.0",
|
||||
"libcutils",
|
||||
"libfmq",
|
||||
"libhidlbase",
|
||||
"libhidlmemory",
|
||||
"libion",
|
||||
"liblog",
|
||||
"libstagefright_foundation",
|
||||
"libutils",
|
||||
],
|
||||
header_libs: [
|
||||
"media_plugin_headers",
|
||||
],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "android.hardware.tv.tuner@1.1-service",
|
||||
vintf_fragments: ["android.hardware.tv.tuner@1.1-service.xml"],
|
||||
defaults: ["tuner_service_defaults@1.1"],
|
||||
init_rc: ["android.hardware.tv.tuner@1.1-service.rc"],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "android.hardware.tv.tuner@1.1-service-lazy",
|
||||
vintf_fragments: ["android.hardware.tv.tuner@1.1-service-lazy.xml"],
|
||||
overrides: ["android.hardware.tv.tuner@1.1-service"],
|
||||
defaults: ["tuner_service_defaults@1.1"],
|
||||
init_rc: ["android.hardware.tv.tuner@1.1-service-lazy.rc"],
|
||||
cflags: ["-DLAZY_SERVICE"],
|
||||
}
|
||||
395
tv/tuner/1.1/default/Demux.cpp
Normal file
395
tv/tuner/1.1/default/Demux.cpp
Normal file
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* 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 "android.hardware.tv.tuner@1.1-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__);
|
||||
|
||||
uint64_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;
|
||||
uint64_t id;
|
||||
Result status;
|
||||
|
||||
sp<V1_1::IFilter> filter_v1_1 = V1_1::IFilter::castFrom(filter);
|
||||
if (filter_v1_1 != NULL) {
|
||||
filter_v1_1->getId64Bit([&](Result result, uint64_t filterId) {
|
||||
id = filterId;
|
||||
status = result;
|
||||
});
|
||||
} else {
|
||||
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<uint64_t>::iterator it;
|
||||
for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) {
|
||||
mDvrPlayback->removePlaybackFilter(*it);
|
||||
}
|
||||
mPlaybackFilterIds.clear();
|
||||
mRecordFilterIds.clear();
|
||||
mFilters.clear();
|
||||
mLastUsedFilterId = -1;
|
||||
|
||||
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<uint64_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(uint64_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<uint64_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<uint64_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<uint64_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<uint64_t>::iterator it;
|
||||
|
||||
for (it = mRecordFilterIds.begin(); it != mRecordFilterIds.end(); it++) {
|
||||
if (mFilters[*it]->startRecordFilterHandler() != Result::SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Result Demux::startFilterHandler(uint64_t filterId) {
|
||||
return mFilters[filterId]->startFilterHandler();
|
||||
}
|
||||
|
||||
void Demux::updateFilterOutput(uint64_t filterId, vector<uint8_t> data) {
|
||||
mFilters[filterId]->updateFilterOutput(data);
|
||||
}
|
||||
|
||||
void Demux::updateMediaFilterOutput(uint64_t filterId, vector<uint8_t> data, uint64_t pts) {
|
||||
updateFilterOutput(filterId, data);
|
||||
mFilters[filterId]->updatePts(pts);
|
||||
}
|
||||
|
||||
uint16_t Demux::getFilterTpid(uint64_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;
|
||||
|
||||
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(uint64_t filterId) {
|
||||
if (mFilters[filterId] == nullptr || mDvrRecord == nullptr ||
|
||||
!mFilters[filterId]->isRecordFilter()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mRecordFilterIds.insert(filterId);
|
||||
mFilters[filterId]->attachFilterToRecord(mDvrRecord);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Demux::detachRecordFilter(uint64_t 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
|
||||
197
tv/tuner/1.1/default/Demux.h
Normal file
197
tv/tuner/1.1/default/Demux.h
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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_HARDWARE_TV_TUNER_V1_1_DEMUX_H_
|
||||
#define ANDROID_HARDWARE_TV_TUNER_V1_1_DEMUX_H_
|
||||
|
||||
#include <fmq/MessageQueue.h>
|
||||
#include <math.h>
|
||||
#include <set>
|
||||
#include "Dvr.h"
|
||||
#include "Filter.h"
|
||||
#include "Frontend.h"
|
||||
#include "TimeFilter.h"
|
||||
#include "Tuner.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace tv {
|
||||
namespace tuner {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::hardware::EventFlag;
|
||||
using ::android::hardware::kSynchronizedReadWrite;
|
||||
using ::android::hardware::MessageQueue;
|
||||
using ::android::hardware::MQDescriptorSync;
|
||||
|
||||
using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
|
||||
|
||||
class Dvr;
|
||||
class Filter;
|
||||
class Frontend;
|
||||
class TimeFilter;
|
||||
class Tuner;
|
||||
|
||||
class Demux : public IDemux {
|
||||
public:
|
||||
Demux(uint32_t demuxId, sp<Tuner> tuner);
|
||||
|
||||
~Demux();
|
||||
|
||||
virtual Return<Result> setFrontendDataSource(uint32_t frontendId) override;
|
||||
|
||||
virtual Return<void> openFilter(const DemuxFilterType& type, uint32_t bufferSize,
|
||||
const sp<IFilterCallback>& cb, openFilter_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<void> openTimeFilter(openTimeFilter_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<void> getAvSyncHwId(const sp<IFilter>& filter,
|
||||
getAvSyncHwId_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<void> getAvSyncTime(AvSyncHwId avSyncHwId, getAvSyncTime_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<Result> close() override;
|
||||
|
||||
virtual Return<void> openDvr(DvrType type, uint32_t bufferSize, const sp<IDvrCallback>& cb,
|
||||
openDvr_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<Result> connectCiCam(uint32_t ciCamId) override;
|
||||
|
||||
virtual Return<Result> disconnectCiCam() override;
|
||||
|
||||
// Functions interacts with Tuner Service
|
||||
void stopFrontendInput();
|
||||
Result removeFilter(uint64_t filterId);
|
||||
bool attachRecordFilter(uint64_t filterId);
|
||||
bool detachRecordFilter(uint64_t filterId);
|
||||
Result startFilterHandler(uint64_t filterId);
|
||||
void updateFilterOutput(uint64_t filterId, vector<uint8_t> data);
|
||||
void updateMediaFilterOutput(uint64_t filterId, vector<uint8_t> data, uint64_t pts);
|
||||
uint16_t getFilterTpid(uint64_t filterId);
|
||||
void setIsRecording(bool isRecording);
|
||||
void startFrontendInputLoop();
|
||||
|
||||
/**
|
||||
* A dispatcher to read and dispatch input data to all the started filters.
|
||||
* Each filter handler handles the data filtering/output writing/filterEvent updating.
|
||||
* Note that recording filters are not included.
|
||||
*/
|
||||
bool startBroadcastFilterDispatcher();
|
||||
void startBroadcastTsFilter(vector<uint8_t> data);
|
||||
|
||||
void sendFrontendInputToRecord(vector<uint8_t> data);
|
||||
bool startRecordFilterDispatcher();
|
||||
|
||||
private:
|
||||
// Tuner service
|
||||
sp<Tuner> mTunerService;
|
||||
|
||||
// Frontend source
|
||||
sp<Frontend> mFrontend;
|
||||
|
||||
// A struct that passes the arguments to a newly created filter thread
|
||||
struct ThreadArgs {
|
||||
Demux* user;
|
||||
uint64_t filterId;
|
||||
};
|
||||
|
||||
static void* __threadLoopFrontend(void* user);
|
||||
void frontendInputThreadLoop();
|
||||
|
||||
/**
|
||||
* To create a FilterMQ with 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.
|
||||
*/
|
||||
void deleteEventFlag();
|
||||
bool readDataFromMQ();
|
||||
|
||||
uint32_t mDemuxId = -1;
|
||||
uint32_t mCiCamId;
|
||||
set<uint64_t> mPcrFilterIds;
|
||||
/**
|
||||
* Record the last used filter id. Initial value is -1.
|
||||
* Filter Id starts with 0.
|
||||
*/
|
||||
uint64_t mLastUsedFilterId = -1;
|
||||
/**
|
||||
* Record all the used playback filter Ids.
|
||||
* Any removed filter id should be removed from this set.
|
||||
*/
|
||||
set<uint64_t> mPlaybackFilterIds;
|
||||
/**
|
||||
* Record all the attached record filter Ids.
|
||||
* Any removed filter id should be removed from this set.
|
||||
*/
|
||||
set<uint64_t> mRecordFilterIds;
|
||||
/**
|
||||
* A list of created Filter sp.
|
||||
* The array number is the filter ID.
|
||||
*/
|
||||
std::map<uint64_t, sp<Filter>> mFilters;
|
||||
|
||||
/**
|
||||
* Local reference to the opened Timer Filter instance.
|
||||
*/
|
||||
sp<TimeFilter> mTimeFilter;
|
||||
|
||||
/**
|
||||
* Local reference to the opened DVR object.
|
||||
*/
|
||||
sp<Dvr> mDvrPlayback;
|
||||
sp<Dvr> mDvrRecord;
|
||||
|
||||
// Thread handlers
|
||||
pthread_t mFrontendInputThread;
|
||||
/**
|
||||
* If a specific filter's writing loop is still running
|
||||
*/
|
||||
bool mFrontendInputThreadRunning;
|
||||
bool mKeepFetchingDataFromFrontend;
|
||||
/**
|
||||
* If the dvr recording is running.
|
||||
*/
|
||||
bool mIsRecording = false;
|
||||
/**
|
||||
* Lock to protect writes to the FMQs
|
||||
*/
|
||||
std::mutex mWriteLock;
|
||||
/**
|
||||
* Lock to protect writes to the input status
|
||||
*/
|
||||
std::mutex mFrontendInputThreadLock;
|
||||
|
||||
// temp handle single PES filter
|
||||
// TODO handle mulptiple Pes filters
|
||||
int mPesSizeLeft = 0;
|
||||
vector<uint8_t> mPesOutput;
|
||||
|
||||
const bool DEBUG_DEMUX = false;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_TV_TUNER_V1_1_DEMUX_H_
|
||||
80
tv/tuner/1.1/default/Descrambler.cpp
Normal file
80
tv/tuner/1.1/default/Descrambler.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 "android.hardware.tv.tuner@1.1-Descrambler"
|
||||
|
||||
#include <android/hardware/tv/tuner/1.0/IFrontendCallback.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include "Descrambler.h"
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace tv {
|
||||
namespace tuner {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
Descrambler::Descrambler() {}
|
||||
|
||||
Descrambler::~Descrambler() {}
|
||||
|
||||
Return<Result> Descrambler::setDemuxSource(uint32_t demuxId) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
if (mDemuxSet) {
|
||||
ALOGW("[ WARN ] Descrambler has already been set with a demux id %" PRIu32,
|
||||
mSourceDemuxId);
|
||||
return Result::INVALID_STATE;
|
||||
}
|
||||
mDemuxSet = true;
|
||||
mSourceDemuxId = static_cast<uint32_t>(demuxId);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Descrambler::setKeyToken(const hidl_vec<uint8_t>& /* keyToken */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Descrambler::addPid(const DemuxPid& /* pid */,
|
||||
const sp<IFilter>& /* optionalSourceFilter */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Descrambler::removePid(const DemuxPid& /* pid */,
|
||||
const sp<IFilter>& /* optionalSourceFilter */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Descrambler::close() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
mDemuxSet = false;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
62
tv/tuner/1.1/default/Descrambler.h
Normal file
62
tv/tuner/1.1/default/Descrambler.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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_HARDWARE_TV_TUNER_V1_1_DESCRAMBLER_H_
|
||||
#define ANDROID_HARDWARE_TV_TUNER_V1_1_DESCRAMBLER_H_
|
||||
|
||||
#include <android/hardware/tv/tuner/1.0/IDescrambler.h>
|
||||
#include <android/hardware/tv/tuner/1.1/ITuner.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace tv {
|
||||
namespace tuner {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
class Descrambler : public IDescrambler {
|
||||
public:
|
||||
Descrambler();
|
||||
|
||||
virtual Return<Result> setDemuxSource(uint32_t demuxId) override;
|
||||
|
||||
virtual Return<Result> setKeyToken(const hidl_vec<uint8_t>& keyToken) override;
|
||||
|
||||
virtual Return<Result> addPid(const DemuxPid& pid,
|
||||
const sp<IFilter>& optionalSourceFilter) override;
|
||||
|
||||
virtual Return<Result> removePid(const DemuxPid& pid,
|
||||
const sp<IFilter>& optionalSourceFilter) override;
|
||||
|
||||
virtual Return<Result> close() override;
|
||||
|
||||
private:
|
||||
virtual ~Descrambler();
|
||||
uint32_t mSourceDemuxId;
|
||||
bool mDemuxSet = false;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_TV_TUNER_V1_DESCRAMBLER_H_
|
||||
498
tv/tuner/1.1/default/Dvr.cpp
Normal file
498
tv/tuner/1.1/default/Dvr.cpp
Normal file
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
* 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 "android.hardware.tv.tuner@1.1-Dvr"
|
||||
|
||||
#include "Dvr.h"
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace tv {
|
||||
namespace tuner {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
#define WAIT_TIMEOUT 3000000000
|
||||
|
||||
Dvr::Dvr() {}
|
||||
|
||||
Dvr::Dvr(DvrType type, uint32_t bufferSize, const sp<IDvrCallback>& cb, sp<Demux> demux) {
|
||||
mType = type;
|
||||
mBufferSize = bufferSize;
|
||||
mCallback = cb;
|
||||
mDemux = demux;
|
||||
}
|
||||
|
||||
Dvr::~Dvr() {}
|
||||
|
||||
Return<void> Dvr::getQueueDesc(getQueueDesc_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
_hidl_cb(Result::SUCCESS, *mDvrMQ->getDesc());
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Dvr::configure(const DvrSettings& settings) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
mDvrSettings = settings;
|
||||
mDvrConfigured = true;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Dvr::attachFilter(const sp<V1_0::IFilter>& filter) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
uint64_t filterId;
|
||||
Result status;
|
||||
|
||||
sp<V1_1::IFilter> filter_v1_1 = V1_1::IFilter::castFrom(filter);
|
||||
if (filter_v1_1 != NULL) {
|
||||
filter_v1_1->getId64Bit([&](Result result, uint64_t id) {
|
||||
filterId = id;
|
||||
status = result;
|
||||
});
|
||||
} else {
|
||||
filter->getId([&](Result result, uint32_t id) {
|
||||
filterId = id;
|
||||
status = result;
|
||||
});
|
||||
}
|
||||
|
||||
if (status != Result::SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// TODO check if the attached filter is a record filter
|
||||
if (!mDemux->attachRecordFilter(filterId)) {
|
||||
return Result::INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Dvr::detachFilter(const sp<V1_0::IFilter>& filter) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
uint64_t filterId;
|
||||
Result status;
|
||||
|
||||
sp<V1_1::IFilter> filter_v1_1 = V1_1::IFilter::castFrom(filter);
|
||||
if (filter_v1_1 != NULL) {
|
||||
filter_v1_1->getId64Bit([&](Result result, uint64_t id) {
|
||||
filterId = id;
|
||||
status = result;
|
||||
});
|
||||
} else {
|
||||
filter->getId([&](Result result, uint32_t id) {
|
||||
filterId = id;
|
||||
status = result;
|
||||
});
|
||||
}
|
||||
|
||||
if (status != Result::SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!mDemux->detachRecordFilter(filterId)) {
|
||||
return Result::INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Dvr::start() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
if (!mCallback) {
|
||||
return Result::NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!mDvrConfigured) {
|
||||
return Result::INVALID_STATE;
|
||||
}
|
||||
|
||||
if (mType == DvrType::PLAYBACK) {
|
||||
pthread_create(&mDvrThread, NULL, __threadLoopPlayback, this);
|
||||
pthread_setname_np(mDvrThread, "playback_waiting_loop");
|
||||
} else if (mType == DvrType::RECORD) {
|
||||
mRecordStatus = RecordStatus::DATA_READY;
|
||||
mDemux->setIsRecording(mType == DvrType::RECORD);
|
||||
}
|
||||
|
||||
// TODO start another thread to send filter status callback to the framework
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Dvr::stop() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
mDvrThreadRunning = false;
|
||||
|
||||
lock_guard<mutex> lock(mDvrThreadLock);
|
||||
|
||||
mIsRecordStarted = false;
|
||||
mDemux->setIsRecording(false);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Dvr::flush() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
mRecordStatus = RecordStatus::DATA_READY;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Dvr::close() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
bool Dvr::createDvrMQ() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
// Create a synchronized FMQ that supports blocking read/write
|
||||
unique_ptr<DvrMQ> tmpDvrMQ = unique_ptr<DvrMQ>(new (nothrow) DvrMQ(mBufferSize, true));
|
||||
if (!tmpDvrMQ->isValid()) {
|
||||
ALOGW("[Dvr] Failed to create FMQ of DVR");
|
||||
return false;
|
||||
}
|
||||
|
||||
mDvrMQ = move(tmpDvrMQ);
|
||||
|
||||
if (EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrEventFlag) != OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
EventFlag* Dvr::getDvrEventFlag() {
|
||||
return mDvrEventFlag;
|
||||
}
|
||||
|
||||
void* Dvr::__threadLoopPlayback(void* user) {
|
||||
Dvr* const self = static_cast<Dvr*>(user);
|
||||
self->playbackThreadLoop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Dvr::playbackThreadLoop() {
|
||||
ALOGD("[Dvr] playback threadLoop start.");
|
||||
lock_guard<mutex> lock(mDvrThreadLock);
|
||||
mDvrThreadRunning = true;
|
||||
|
||||
while (mDvrThreadRunning) {
|
||||
uint32_t efState = 0;
|
||||
status_t status =
|
||||
mDvrEventFlag->wait(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY),
|
||||
&efState, WAIT_TIMEOUT, true /* retry on spurious wake */);
|
||||
if (status != OK) {
|
||||
ALOGD("[Dvr] wait for data ready on the playback FMQ");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mDvrSettings.playback().dataFormat == DataFormat::ES) {
|
||||
if (!processEsDataOnPlayback(false /*isVirtualFrontend*/, false /*isRecording*/)) {
|
||||
ALOGE("[Dvr] playback es data failed to be filtered. Ending thread");
|
||||
break;
|
||||
}
|
||||
maySendPlaybackStatusCallback();
|
||||
}
|
||||
// Our current implementation filter the data and write it into the filter FMQ immediately
|
||||
// after the DATA_READY from the VTS/framework
|
||||
if (!readPlaybackFMQ(false /*isVirtualFrontend*/, false /*isRecording*/) ||
|
||||
!startFilterDispatcher(false /*isVirtualFrontend*/, false /*isRecording*/)) {
|
||||
ALOGE("[Dvr] playback data failed to be filtered. Ending thread");
|
||||
break;
|
||||
}
|
||||
|
||||
maySendPlaybackStatusCallback();
|
||||
}
|
||||
|
||||
mDvrThreadRunning = false;
|
||||
ALOGD("[Dvr] playback thread ended.");
|
||||
}
|
||||
|
||||
void Dvr::maySendPlaybackStatusCallback() {
|
||||
lock_guard<mutex> lock(mPlaybackStatusLock);
|
||||
int availableToRead = mDvrMQ->availableToRead();
|
||||
int availableToWrite = mDvrMQ->availableToWrite();
|
||||
|
||||
PlaybackStatus newStatus = checkPlaybackStatusChange(availableToWrite, availableToRead,
|
||||
mDvrSettings.playback().highThreshold,
|
||||
mDvrSettings.playback().lowThreshold);
|
||||
if (mPlaybackStatus != newStatus) {
|
||||
mCallback->onPlaybackStatus(newStatus);
|
||||
mPlaybackStatus = newStatus;
|
||||
}
|
||||
}
|
||||
|
||||
PlaybackStatus Dvr::checkPlaybackStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
|
||||
uint32_t highThreshold, uint32_t lowThreshold) {
|
||||
if (availableToWrite == 0) {
|
||||
return PlaybackStatus::SPACE_FULL;
|
||||
} else if (availableToRead > highThreshold) {
|
||||
return PlaybackStatus::SPACE_ALMOST_FULL;
|
||||
} else if (availableToRead < lowThreshold) {
|
||||
return PlaybackStatus::SPACE_ALMOST_EMPTY;
|
||||
} else if (availableToRead == 0) {
|
||||
return PlaybackStatus::SPACE_EMPTY;
|
||||
}
|
||||
return mPlaybackStatus;
|
||||
}
|
||||
|
||||
bool Dvr::readPlaybackFMQ(bool isVirtualFrontend, bool isRecording) {
|
||||
// Read playback data from the input FMQ
|
||||
int size = mDvrMQ->availableToRead();
|
||||
int playbackPacketSize = mDvrSettings.playback().packetSize;
|
||||
vector<uint8_t> dataOutputBuffer;
|
||||
dataOutputBuffer.resize(playbackPacketSize);
|
||||
// Dispatch the packet to the PID matching filter output buffer
|
||||
for (int i = 0; i < size / playbackPacketSize; i++) {
|
||||
if (!mDvrMQ->read(dataOutputBuffer.data(), playbackPacketSize)) {
|
||||
return false;
|
||||
}
|
||||
if (isVirtualFrontend) {
|
||||
if (isRecording) {
|
||||
mDemux->sendFrontendInputToRecord(dataOutputBuffer);
|
||||
} else {
|
||||
mDemux->startBroadcastTsFilter(dataOutputBuffer);
|
||||
}
|
||||
} else {
|
||||
startTpidFilter(dataOutputBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Dvr::processEsDataOnPlayback(bool isVirtualFrontend, bool isRecording) {
|
||||
// Read ES from the DVR FMQ
|
||||
// Note that currently we only provides ES with metaData in a specific format to be parsed.
|
||||
// The ES size should be smaller than the Playback FMQ size to avoid reading truncated data.
|
||||
int size = mDvrMQ->availableToRead();
|
||||
vector<uint8_t> dataOutputBuffer;
|
||||
dataOutputBuffer.resize(size);
|
||||
if (!mDvrMQ->read(dataOutputBuffer.data(), size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int metaDataSize = size;
|
||||
int totalFrames = 0;
|
||||
int videoEsDataSize = 0;
|
||||
int audioEsDataSize = 0;
|
||||
int audioPid = 0;
|
||||
int videoPid = 0;
|
||||
|
||||
vector<MediaEsMetaData> esMeta;
|
||||
int videoReadPointer = 0;
|
||||
int audioReadPointer = 0;
|
||||
int frameCount = 0;
|
||||
// Get meta data from the es
|
||||
for (int i = 0; i < metaDataSize; i++) {
|
||||
switch (dataOutputBuffer[i]) {
|
||||
case 'm':
|
||||
metaDataSize = 0;
|
||||
getMetaDataValue(i, dataOutputBuffer.data(), metaDataSize);
|
||||
videoReadPointer = metaDataSize;
|
||||
continue;
|
||||
case 'l':
|
||||
getMetaDataValue(i, dataOutputBuffer.data(), totalFrames);
|
||||
esMeta.resize(totalFrames);
|
||||
continue;
|
||||
case 'V':
|
||||
getMetaDataValue(i, dataOutputBuffer.data(), videoEsDataSize);
|
||||
audioReadPointer = metaDataSize + videoEsDataSize;
|
||||
continue;
|
||||
case 'A':
|
||||
getMetaDataValue(i, dataOutputBuffer.data(), audioEsDataSize);
|
||||
continue;
|
||||
case 'p':
|
||||
if (dataOutputBuffer[++i] == 'a') {
|
||||
getMetaDataValue(i, dataOutputBuffer.data(), audioPid);
|
||||
} else if (dataOutputBuffer[i] == 'v') {
|
||||
getMetaDataValue(i, dataOutputBuffer.data(), videoPid);
|
||||
}
|
||||
continue;
|
||||
case 'v':
|
||||
case 'a':
|
||||
if (dataOutputBuffer[i + 1] != ',') {
|
||||
ALOGE("[Dvr] Invalid format meta data.");
|
||||
return false;
|
||||
}
|
||||
esMeta[frameCount] = {
|
||||
.isAudio = dataOutputBuffer[i] == 'a' ? true : false,
|
||||
};
|
||||
i += 5; // Move to Len
|
||||
getMetaDataValue(i, dataOutputBuffer.data(), esMeta[frameCount].len);
|
||||
if (esMeta[frameCount].isAudio) {
|
||||
esMeta[frameCount].startIndex = audioReadPointer;
|
||||
audioReadPointer += esMeta[frameCount].len;
|
||||
} else {
|
||||
esMeta[frameCount].startIndex = videoReadPointer;
|
||||
videoReadPointer += esMeta[frameCount].len;
|
||||
}
|
||||
i += 4; // move to PTS
|
||||
getMetaDataValue(i, dataOutputBuffer.data(), esMeta[frameCount].pts);
|
||||
frameCount++;
|
||||
continue;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (frameCount != totalFrames) {
|
||||
ALOGE("[Dvr] Invalid meta data, frameCount=%d, totalFrames reported=%d", frameCount,
|
||||
totalFrames);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (metaDataSize + audioEsDataSize + videoEsDataSize != size) {
|
||||
ALOGE("[Dvr] Invalid meta data, metaSize=%d, videoSize=%d, audioSize=%d, totolSize=%d",
|
||||
metaDataSize, videoEsDataSize, audioEsDataSize, size);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read es raw data from the FMQ per meta data built previously
|
||||
vector<uint8_t> frameData;
|
||||
map<uint64_t, sp<IFilter>>::iterator it;
|
||||
int pid = 0;
|
||||
for (int i = 0; i < totalFrames; i++) {
|
||||
frameData.resize(esMeta[i].len);
|
||||
pid = esMeta[i].isAudio ? audioPid : videoPid;
|
||||
memcpy(dataOutputBuffer.data() + esMeta[i].startIndex, frameData.data(), esMeta[i].len);
|
||||
// Send to the media filter
|
||||
if (isVirtualFrontend && isRecording) {
|
||||
// TODO validate record
|
||||
mDemux->sendFrontendInputToRecord(frameData);
|
||||
} else {
|
||||
for (it = mFilters.begin(); it != mFilters.end(); it++) {
|
||||
if (pid == mDemux->getFilterTpid(it->first)) {
|
||||
mDemux->updateMediaFilterOutput(it->first, frameData,
|
||||
static_cast<uint64_t>(esMeta[i].pts));
|
||||
startFilterDispatcher(isVirtualFrontend, isRecording);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Dvr::getMetaDataValue(int& index, uint8_t* dataOutputBuffer, int& value) {
|
||||
index += 2; // Move the pointer across the ":" to the value
|
||||
while (dataOutputBuffer[index] != ',' && dataOutputBuffer[index] != '\n') {
|
||||
value = ((dataOutputBuffer[index++] - 48) + value * 10);
|
||||
}
|
||||
}
|
||||
|
||||
void Dvr::startTpidFilter(vector<uint8_t> data) {
|
||||
map<uint64_t, sp<IFilter>>::iterator it;
|
||||
for (it = mFilters.begin(); it != mFilters.end(); it++) {
|
||||
uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff));
|
||||
if (DEBUG_DVR) {
|
||||
ALOGW("[Dvr] start ts filter pid: %d", pid);
|
||||
}
|
||||
if (pid == mDemux->getFilterTpid(it->first)) {
|
||||
mDemux->updateFilterOutput(it->first, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Dvr::startFilterDispatcher(bool isVirtualFrontend, bool isRecording) {
|
||||
if (isVirtualFrontend) {
|
||||
if (isRecording) {
|
||||
return mDemux->startRecordFilterDispatcher();
|
||||
} else {
|
||||
return mDemux->startBroadcastFilterDispatcher();
|
||||
}
|
||||
}
|
||||
|
||||
map<uint64_t, sp<IFilter>>::iterator it;
|
||||
// Handle the output data per filter type
|
||||
for (it = mFilters.begin(); it != mFilters.end(); it++) {
|
||||
if (mDemux->startFilterHandler(it->first) != Result::SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Dvr::writeRecordFMQ(const vector<uint8_t>& data) {
|
||||
lock_guard<mutex> lock(mWriteLock);
|
||||
if (mRecordStatus == RecordStatus::OVERFLOW) {
|
||||
ALOGW("[Dvr] stops writing and wait for the client side flushing.");
|
||||
return true;
|
||||
}
|
||||
if (mDvrMQ->write(data.data(), data.size())) {
|
||||
mDvrEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
|
||||
maySendRecordStatusCallback();
|
||||
return true;
|
||||
}
|
||||
|
||||
maySendRecordStatusCallback();
|
||||
return false;
|
||||
}
|
||||
|
||||
void Dvr::maySendRecordStatusCallback() {
|
||||
lock_guard<mutex> lock(mRecordStatusLock);
|
||||
int availableToRead = mDvrMQ->availableToRead();
|
||||
int availableToWrite = mDvrMQ->availableToWrite();
|
||||
|
||||
RecordStatus newStatus = checkRecordStatusChange(availableToWrite, availableToRead,
|
||||
mDvrSettings.record().highThreshold,
|
||||
mDvrSettings.record().lowThreshold);
|
||||
if (mRecordStatus != newStatus) {
|
||||
mCallback->onRecordStatus(newStatus);
|
||||
mRecordStatus = newStatus;
|
||||
}
|
||||
}
|
||||
|
||||
RecordStatus Dvr::checkRecordStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
|
||||
uint32_t highThreshold, uint32_t lowThreshold) {
|
||||
if (availableToWrite == 0) {
|
||||
return DemuxFilterStatus::OVERFLOW;
|
||||
} else if (availableToRead > highThreshold) {
|
||||
return DemuxFilterStatus::HIGH_WATER;
|
||||
} else if (availableToRead < lowThreshold) {
|
||||
return DemuxFilterStatus::LOW_WATER;
|
||||
}
|
||||
return mRecordStatus;
|
||||
}
|
||||
|
||||
bool Dvr::addPlaybackFilter(uint64_t filterId, sp<IFilter> filter) {
|
||||
mFilters[filterId] = filter;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Dvr::removePlaybackFilter(uint64_t filterId) {
|
||||
mFilters.erase(filterId);
|
||||
return true;
|
||||
}
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
167
tv/tuner/1.1/default/Dvr.h
Normal file
167
tv/tuner/1.1/default/Dvr.h
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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_HARDWARE_TV_TUNER_V1_1_DVR_H_
|
||||
#define ANDROID_HARDWARE_TV_TUNER_V1_1_DVR_H_
|
||||
|
||||
#include <fmq/MessageQueue.h>
|
||||
#include <math.h>
|
||||
#include <set>
|
||||
#include "Demux.h"
|
||||
#include "Frontend.h"
|
||||
#include "Tuner.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace tv {
|
||||
namespace tuner {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::hardware::EventFlag;
|
||||
using ::android::hardware::kSynchronizedReadWrite;
|
||||
using ::android::hardware::MessageQueue;
|
||||
using ::android::hardware::MQDescriptorSync;
|
||||
|
||||
using DvrMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
|
||||
|
||||
struct MediaEsMetaData {
|
||||
bool isAudio;
|
||||
int startIndex;
|
||||
int len;
|
||||
int pts;
|
||||
};
|
||||
|
||||
class Demux;
|
||||
class Filter;
|
||||
class Frontend;
|
||||
class Tuner;
|
||||
|
||||
class Dvr : public IDvr {
|
||||
public:
|
||||
Dvr();
|
||||
|
||||
Dvr(DvrType type, uint32_t bufferSize, const sp<IDvrCallback>& cb, sp<Demux> demux);
|
||||
|
||||
~Dvr();
|
||||
|
||||
virtual Return<void> getQueueDesc(getQueueDesc_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<Result> configure(const DvrSettings& settings) override;
|
||||
|
||||
virtual Return<Result> attachFilter(const sp<IFilter>& filter) override;
|
||||
|
||||
virtual Return<Result> detachFilter(const sp<IFilter>& filter) override;
|
||||
|
||||
virtual Return<Result> start() override;
|
||||
|
||||
virtual Return<Result> stop() override;
|
||||
|
||||
virtual Return<Result> flush() override;
|
||||
|
||||
virtual Return<Result> close() override;
|
||||
|
||||
/**
|
||||
* To create a DvrMQ and its Event Flag.
|
||||
*
|
||||
* Return false is any of the above processes fails.
|
||||
*/
|
||||
bool createDvrMQ();
|
||||
void sendBroadcastInputToDvrRecord(vector<uint8_t> byteBuffer);
|
||||
bool writeRecordFMQ(const std::vector<uint8_t>& data);
|
||||
bool addPlaybackFilter(uint64_t filterId, sp<IFilter> filter);
|
||||
bool removePlaybackFilter(uint64_t filterId);
|
||||
bool readPlaybackFMQ(bool isVirtualFrontend, bool isRecording);
|
||||
bool processEsDataOnPlayback(bool isVirtualFrontend, bool isRecording);
|
||||
bool startFilterDispatcher(bool isVirtualFrontend, bool isRecording);
|
||||
EventFlag* getDvrEventFlag();
|
||||
DvrSettings getSettings() { return mDvrSettings; }
|
||||
|
||||
private:
|
||||
// Demux service
|
||||
sp<Demux> mDemux;
|
||||
|
||||
DvrType mType;
|
||||
uint32_t mBufferSize;
|
||||
sp<IDvrCallback> mCallback;
|
||||
std::map<uint64_t, sp<IFilter>> mFilters;
|
||||
|
||||
void deleteEventFlag();
|
||||
bool readDataFromMQ();
|
||||
void getMetaDataValue(int& index, uint8_t* dataOutputBuffer, int& value);
|
||||
void maySendPlaybackStatusCallback();
|
||||
void maySendRecordStatusCallback();
|
||||
PlaybackStatus checkPlaybackStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
|
||||
uint32_t highThreshold, uint32_t lowThreshold);
|
||||
RecordStatus checkRecordStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
|
||||
uint32_t highThreshold, uint32_t lowThreshold);
|
||||
/**
|
||||
* A dispatcher to read and dispatch input data to all the started filters.
|
||||
* Each filter handler handles the data filtering/output writing/filterEvent updating.
|
||||
*/
|
||||
void startTpidFilter(vector<uint8_t> data);
|
||||
static void* __threadLoopPlayback(void* user);
|
||||
static void* __threadLoopRecord(void* user);
|
||||
void playbackThreadLoop();
|
||||
void recordThreadLoop();
|
||||
|
||||
unique_ptr<DvrMQ> mDvrMQ;
|
||||
EventFlag* mDvrEventFlag;
|
||||
/**
|
||||
* Demux callbacks used on filter events or IO buffer status
|
||||
*/
|
||||
bool mDvrConfigured = false;
|
||||
DvrSettings mDvrSettings;
|
||||
|
||||
// Thread handlers
|
||||
pthread_t mDvrThread;
|
||||
|
||||
// FMQ status local records
|
||||
PlaybackStatus mPlaybackStatus;
|
||||
RecordStatus mRecordStatus;
|
||||
/**
|
||||
* If a specific filter's writing loop is still running
|
||||
*/
|
||||
bool mDvrThreadRunning;
|
||||
bool mKeepFetchingDataFromFrontend;
|
||||
/**
|
||||
* Lock to protect writes to the FMQs
|
||||
*/
|
||||
std::mutex mWriteLock;
|
||||
/**
|
||||
* Lock to protect writes to the input status
|
||||
*/
|
||||
std::mutex mPlaybackStatusLock;
|
||||
std::mutex mRecordStatusLock;
|
||||
std::mutex mDvrThreadLock;
|
||||
|
||||
const bool DEBUG_DVR = false;
|
||||
|
||||
// Booleans to check if recording is running.
|
||||
// Recording is ready when both of the following are set to true.
|
||||
bool mIsRecordStarted = false;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_TV_TUNER_V1_1_DVR_H_
|
||||
672
tv/tuner/1.1/default/Filter.cpp
Normal file
672
tv/tuner/1.1/default/Filter.cpp
Normal file
@@ -0,0 +1,672 @@
|
||||
/*
|
||||
* 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 "android.hardware.tv.tuner@1.1-Filter"
|
||||
|
||||
#include "Filter.h"
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace tv {
|
||||
namespace tuner {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
#define WAIT_TIMEOUT 3000000000
|
||||
|
||||
Filter::Filter() {}
|
||||
|
||||
Filter::Filter(DemuxFilterType type, uint64_t filterId, uint32_t bufferSize,
|
||||
const sp<IFilterCallback>& cb, sp<Demux> demux) {
|
||||
mType = type;
|
||||
mFilterId = filterId;
|
||||
mBufferSize = bufferSize;
|
||||
mCallback = cb;
|
||||
mDemux = demux;
|
||||
|
||||
switch (mType.mainType) {
|
||||
case DemuxFilterMainType::TS:
|
||||
if (mType.subType.tsFilterType() == DemuxTsFilterType::AUDIO ||
|
||||
mType.subType.tsFilterType() == DemuxTsFilterType::VIDEO) {
|
||||
mIsMediaFilter = true;
|
||||
}
|
||||
if (mType.subType.tsFilterType() == DemuxTsFilterType::PCR) {
|
||||
mIsPcrFilter = true;
|
||||
}
|
||||
if (mType.subType.tsFilterType() == DemuxTsFilterType::RECORD) {
|
||||
mIsRecordFilter = true;
|
||||
}
|
||||
break;
|
||||
case DemuxFilterMainType::MMTP:
|
||||
if (mType.subType.mmtpFilterType() == DemuxMmtpFilterType::AUDIO ||
|
||||
mType.subType.mmtpFilterType() == DemuxMmtpFilterType::VIDEO) {
|
||||
mIsMediaFilter = true;
|
||||
}
|
||||
if (mType.subType.mmtpFilterType() == DemuxMmtpFilterType::RECORD) {
|
||||
mIsRecordFilter = true;
|
||||
}
|
||||
break;
|
||||
case DemuxFilterMainType::IP:
|
||||
break;
|
||||
case DemuxFilterMainType::TLV:
|
||||
break;
|
||||
case DemuxFilterMainType::ALP:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Filter::~Filter() {}
|
||||
|
||||
Return<void> Filter::getId64Bit(getId64Bit_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
_hidl_cb(Result::SUCCESS, mFilterId);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Filter::getId(getId_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
_hidl_cb(Result::SUCCESS, static_cast<uint32_t>(mFilterId));
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Filter::setDataSource(const sp<V1_0::IFilter>& filter) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
mDataSource = filter;
|
||||
mIsDataSourceDemux = false;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<void> Filter::getQueueDesc(getQueueDesc_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
mIsUsingFMQ = true;
|
||||
|
||||
_hidl_cb(Result::SUCCESS, *mFilterMQ->getDesc());
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Filter::configure(const DemuxFilterSettings& settings) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
mFilterSettings = settings;
|
||||
switch (mType.mainType) {
|
||||
case DemuxFilterMainType::TS:
|
||||
mTpid = settings.ts().tpid;
|
||||
break;
|
||||
case DemuxFilterMainType::MMTP:
|
||||
break;
|
||||
case DemuxFilterMainType::IP:
|
||||
break;
|
||||
case DemuxFilterMainType::TLV:
|
||||
break;
|
||||
case DemuxFilterMainType::ALP:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Filter::start() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return startFilterLoop();
|
||||
}
|
||||
|
||||
Return<Result> Filter::stop() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
mFilterThreadRunning = false;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mFilterThreadLock);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Filter::flush() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
// temp implementation to flush the FMQ
|
||||
int size = mFilterMQ->availableToRead();
|
||||
char* buffer = new char[size];
|
||||
mFilterMQ->read((unsigned char*)&buffer[0], size);
|
||||
delete[] buffer;
|
||||
mFilterStatus = DemuxFilterStatus::DATA_READY;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Filter::releaseAvHandle(const hidl_handle& /*avMemory*/, uint64_t avDataId) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
if (mDataId2Avfd.find(avDataId) == mDataId2Avfd.end()) {
|
||||
return Result::INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
::close(mDataId2Avfd[avDataId]);
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Filter::close() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return mDemux->removeFilter(mFilterId);
|
||||
}
|
||||
|
||||
bool Filter::createFilterMQ() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
// Create a synchronized FMQ that supports blocking read/write
|
||||
std::unique_ptr<FilterMQ> tmpFilterMQ =
|
||||
std::unique_ptr<FilterMQ>(new (std::nothrow) FilterMQ(mBufferSize, true));
|
||||
if (!tmpFilterMQ->isValid()) {
|
||||
ALOGW("[Filter] Failed to create FMQ of filter with id: %" PRIu64, mFilterId);
|
||||
return false;
|
||||
}
|
||||
|
||||
mFilterMQ = std::move(tmpFilterMQ);
|
||||
|
||||
if (EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterEventFlag) != OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Result Filter::startFilterLoop() {
|
||||
pthread_create(&mFilterThread, NULL, __threadLoopFilter, this);
|
||||
pthread_setname_np(mFilterThread, "filter_waiting_loop");
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
void* Filter::__threadLoopFilter(void* user) {
|
||||
Filter* const self = static_cast<Filter*>(user);
|
||||
self->filterThreadLoop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Filter::filterThreadLoop() {
|
||||
ALOGD("[Filter] filter %" PRIu64 " threadLoop start.", mFilterId);
|
||||
std::lock_guard<std::mutex> lock(mFilterThreadLock);
|
||||
mFilterThreadRunning = true;
|
||||
|
||||
// For the first time of filter output, implementation needs to send the filter
|
||||
// Event Callback without waiting for the DATA_CONSUMED to init the process.
|
||||
while (mFilterThreadRunning) {
|
||||
if (mFilterEvent.events.size() == 0) {
|
||||
if (DEBUG_FILTER) {
|
||||
ALOGD("[Filter] wait for filter data output.");
|
||||
}
|
||||
usleep(1000 * 1000);
|
||||
continue;
|
||||
}
|
||||
// After successfully write, send a callback and wait for the read to be done
|
||||
mCallback->onFilterEvent(mFilterEvent);
|
||||
freeAvHandle();
|
||||
mFilterEvent.events.resize(0);
|
||||
mFilterStatus = DemuxFilterStatus::DATA_READY;
|
||||
if (mCallback == nullptr) {
|
||||
ALOGD("[Filter] filter %" PRIu64 " does not hava callback. Ending thread", mFilterId);
|
||||
break;
|
||||
}
|
||||
mCallback->onFilterStatus(mFilterStatus);
|
||||
break;
|
||||
}
|
||||
|
||||
while (mFilterThreadRunning) {
|
||||
uint32_t efState = 0;
|
||||
// We do not wait for the last round of written 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++) {
|
||||
while (mFilterThreadRunning && mIsUsingFMQ) {
|
||||
status_t status = mFilterEventFlag->wait(
|
||||
static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED), &efState,
|
||||
WAIT_TIMEOUT, true /* retry on spurious wake */);
|
||||
if (status != OK) {
|
||||
ALOGD("[Filter] wait for data consumed");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
maySendFilterStatusCallback();
|
||||
|
||||
while (mFilterThreadRunning) {
|
||||
std::lock_guard<std::mutex> lock(mFilterEventLock);
|
||||
if (mFilterEvent.events.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
// After successfully write, send a callback and wait for the read to be done
|
||||
mCallback->onFilterEvent(mFilterEvent);
|
||||
mFilterEvent.events.resize(0);
|
||||
break;
|
||||
}
|
||||
// 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("[Filter] filter %" PRIu64 " writing done. Ending thread", mFilterId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mFilterThreadRunning = false;
|
||||
}
|
||||
|
||||
ALOGD("[Filter] filter thread ended.");
|
||||
}
|
||||
|
||||
void Filter::freeAvHandle() {
|
||||
if (!mIsMediaFilter) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < mFilterEvent.events.size(); i++) {
|
||||
::close(mFilterEvent.events[i].media().avMemory.getNativeHandle()->data[0]);
|
||||
native_handle_close(mFilterEvent.events[i].media().avMemory.getNativeHandle());
|
||||
}
|
||||
}
|
||||
|
||||
void Filter::maySendFilterStatusCallback() {
|
||||
if (!mIsUsingFMQ) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(mFilterStatusLock);
|
||||
int availableToRead = mFilterMQ->availableToRead();
|
||||
int availableToWrite = mFilterMQ->availableToWrite();
|
||||
int fmqSize = mFilterMQ->getQuantumCount();
|
||||
|
||||
DemuxFilterStatus newStatus = checkFilterStatusChange(
|
||||
availableToWrite, availableToRead, ceil(fmqSize * 0.75), ceil(fmqSize * 0.25));
|
||||
if (mFilterStatus != newStatus) {
|
||||
mCallback->onFilterStatus(newStatus);
|
||||
mFilterStatus = newStatus;
|
||||
}
|
||||
}
|
||||
|
||||
DemuxFilterStatus Filter::checkFilterStatusChange(uint32_t availableToWrite,
|
||||
uint32_t availableToRead, uint32_t highThreshold,
|
||||
uint32_t lowThreshold) {
|
||||
if (availableToWrite == 0) {
|
||||
return DemuxFilterStatus::OVERFLOW;
|
||||
} else if (availableToRead > highThreshold) {
|
||||
return DemuxFilterStatus::HIGH_WATER;
|
||||
} else if (availableToRead < lowThreshold) {
|
||||
return DemuxFilterStatus::LOW_WATER;
|
||||
}
|
||||
return mFilterStatus;
|
||||
}
|
||||
|
||||
uint16_t Filter::getTpid() {
|
||||
return mTpid;
|
||||
}
|
||||
|
||||
void Filter::updateFilterOutput(vector<uint8_t> data) {
|
||||
std::lock_guard<std::mutex> lock(mFilterOutputLock);
|
||||
mFilterOutput.insert(mFilterOutput.end(), data.begin(), data.end());
|
||||
}
|
||||
|
||||
void Filter::updatePts(uint64_t pts) {
|
||||
std::lock_guard<std::mutex> lock(mFilterOutputLock);
|
||||
mPts = pts;
|
||||
}
|
||||
|
||||
void Filter::updateRecordOutput(vector<uint8_t> data) {
|
||||
std::lock_guard<std::mutex> lock(mRecordFilterOutputLock);
|
||||
mRecordFilterOutput.insert(mRecordFilterOutput.end(), data.begin(), data.end());
|
||||
}
|
||||
|
||||
Result Filter::startFilterHandler() {
|
||||
std::lock_guard<std::mutex> lock(mFilterOutputLock);
|
||||
switch (mType.mainType) {
|
||||
case DemuxFilterMainType::TS:
|
||||
switch (mType.subType.tsFilterType()) {
|
||||
case DemuxTsFilterType::UNDEFINED:
|
||||
break;
|
||||
case DemuxTsFilterType::SECTION:
|
||||
startSectionFilterHandler();
|
||||
break;
|
||||
case DemuxTsFilterType::PES:
|
||||
startPesFilterHandler();
|
||||
break;
|
||||
case DemuxTsFilterType::TS:
|
||||
startTsFilterHandler();
|
||||
break;
|
||||
case DemuxTsFilterType::AUDIO:
|
||||
case DemuxTsFilterType::VIDEO:
|
||||
startMediaFilterHandler();
|
||||
break;
|
||||
case DemuxTsFilterType::PCR:
|
||||
startPcrFilterHandler();
|
||||
break;
|
||||
case DemuxTsFilterType::TEMI:
|
||||
startTemiFilterHandler();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case DemuxFilterMainType::MMTP:
|
||||
/*mmtpSettings*/
|
||||
break;
|
||||
case DemuxFilterMainType::IP:
|
||||
/*ipSettings*/
|
||||
break;
|
||||
case DemuxFilterMainType::TLV:
|
||||
/*tlvSettings*/
|
||||
break;
|
||||
case DemuxFilterMainType::ALP:
|
||||
/*alpSettings*/
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Result Filter::startSectionFilterHandler() {
|
||||
if (mFilterOutput.empty()) {
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
if (!writeSectionsAndCreateEvent(mFilterOutput)) {
|
||||
ALOGD("[Filter] filter %" PRIu64 " fails to write into FMQ. Ending thread", mFilterId);
|
||||
return Result::UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mFilterOutput.clear();
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Result Filter::startPesFilterHandler() {
|
||||
std::lock_guard<std::mutex> lock(mFilterEventLock);
|
||||
if (mFilterOutput.empty()) {
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
for (int i = 0; i < mFilterOutput.size(); i += 188) {
|
||||
if (mPesSizeLeft == 0) {
|
||||
uint32_t prefix = (mFilterOutput[i + 4] << 16) | (mFilterOutput[i + 5] << 8) |
|
||||
mFilterOutput[i + 6];
|
||||
if (DEBUG_FILTER) {
|
||||
ALOGD("[Filter] prefix %d", prefix);
|
||||
}
|
||||
if (prefix == 0x000001) {
|
||||
// TODO handle mulptiple Pes filters
|
||||
mPesSizeLeft = (mFilterOutput[i + 8] << 8) | mFilterOutput[i + 9];
|
||||
mPesSizeLeft += 6;
|
||||
if (DEBUG_FILTER) {
|
||||
ALOGD("[Filter] pes data length %d", mPesSizeLeft);
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
int endPoint = min(184, mPesSizeLeft);
|
||||
// append data and check size
|
||||
vector<uint8_t>::const_iterator first = mFilterOutput.begin() + i + 4;
|
||||
vector<uint8_t>::const_iterator last = mFilterOutput.begin() + i + 4 + endPoint;
|
||||
mPesOutput.insert(mPesOutput.end(), first, last);
|
||||
// size does not match then continue
|
||||
mPesSizeLeft -= endPoint;
|
||||
if (DEBUG_FILTER) {
|
||||
ALOGD("[Filter] pes data left %d", mPesSizeLeft);
|
||||
}
|
||||
if (mPesSizeLeft > 0) {
|
||||
continue;
|
||||
}
|
||||
// size match then create event
|
||||
if (!writeDataToFilterMQ(mPesOutput)) {
|
||||
ALOGD("[Filter] pes data write failed");
|
||||
mFilterOutput.clear();
|
||||
return Result::INVALID_STATE;
|
||||
}
|
||||
maySendFilterStatusCallback();
|
||||
DemuxFilterPesEvent pesEvent;
|
||||
pesEvent = {
|
||||
// temp dump meta data
|
||||
.streamId = mPesOutput[3],
|
||||
.dataLength = static_cast<uint16_t>(mPesOutput.size()),
|
||||
};
|
||||
if (DEBUG_FILTER) {
|
||||
ALOGD("[Filter] assembled pes data length %d", pesEvent.dataLength);
|
||||
}
|
||||
|
||||
int size = mFilterEvent.events.size();
|
||||
mFilterEvent.events.resize(size + 1);
|
||||
mFilterEvent.events[size].pes(pesEvent);
|
||||
mPesOutput.clear();
|
||||
}
|
||||
|
||||
mFilterOutput.clear();
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Result Filter::startTsFilterHandler() {
|
||||
// TODO handle starting TS filter
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Result Filter::startMediaFilterHandler() {
|
||||
std::lock_guard<std::mutex> lock(mFilterEventLock);
|
||||
if (mFilterOutput.empty()) {
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
if (mPts) {
|
||||
return createMediaFilterEventWithIon(mFilterOutput);
|
||||
}
|
||||
|
||||
for (int i = 0; i < mFilterOutput.size(); i += 188) {
|
||||
if (mPesSizeLeft == 0) {
|
||||
uint32_t prefix = (mFilterOutput[i + 4] << 16) | (mFilterOutput[i + 5] << 8) |
|
||||
mFilterOutput[i + 6];
|
||||
if (DEBUG_FILTER) {
|
||||
ALOGD("[Filter] prefix %d", prefix);
|
||||
}
|
||||
if (prefix == 0x000001) {
|
||||
// TODO handle mulptiple Pes filters
|
||||
mPesSizeLeft = (mFilterOutput[i + 8] << 8) | mFilterOutput[i + 9];
|
||||
mPesSizeLeft += 6;
|
||||
if (DEBUG_FILTER) {
|
||||
ALOGD("[Filter] pes data length %d", mPesSizeLeft);
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
int endPoint = min(184, mPesSizeLeft);
|
||||
// append data and check size
|
||||
vector<uint8_t>::const_iterator first = mFilterOutput.begin() + i + 4;
|
||||
vector<uint8_t>::const_iterator last = mFilterOutput.begin() + i + 4 + endPoint;
|
||||
mPesOutput.insert(mPesOutput.end(), first, last);
|
||||
// size does not match then continue
|
||||
mPesSizeLeft -= endPoint;
|
||||
if (DEBUG_FILTER) {
|
||||
ALOGD("[Filter] pes data left %d", mPesSizeLeft);
|
||||
}
|
||||
if (mPesSizeLeft > 0 || mAvBufferCopyCount++ < 10) {
|
||||
continue;
|
||||
}
|
||||
|
||||
createMediaFilterEventWithIon(mPesOutput);
|
||||
}
|
||||
|
||||
mFilterOutput.clear();
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Result Filter::createMediaFilterEventWithIon(vector<uint8_t> output) {
|
||||
int av_fd = createAvIonFd(output.size());
|
||||
if (av_fd == -1) {
|
||||
return Result::UNKNOWN_ERROR;
|
||||
}
|
||||
// copy the filtered data to the buffer
|
||||
uint8_t* avBuffer = getIonBuffer(av_fd, output.size());
|
||||
if (avBuffer == NULL) {
|
||||
return Result::UNKNOWN_ERROR;
|
||||
}
|
||||
memcpy(avBuffer, output.data(), output.size() * sizeof(uint8_t));
|
||||
|
||||
native_handle_t* nativeHandle = createNativeHandle(av_fd);
|
||||
if (nativeHandle == NULL) {
|
||||
return Result::UNKNOWN_ERROR;
|
||||
}
|
||||
hidl_handle handle;
|
||||
handle.setTo(nativeHandle, /*shouldOwn=*/true);
|
||||
|
||||
// Create a dataId and add a <dataId, av_fd> pair into the dataId2Avfd map
|
||||
uint64_t dataId = mLastUsedDataId++ /*createdUID*/;
|
||||
mDataId2Avfd[dataId] = dup(av_fd);
|
||||
|
||||
// Create mediaEvent and send callback
|
||||
DemuxFilterMediaEvent mediaEvent;
|
||||
mediaEvent = {
|
||||
.avMemory = std::move(handle),
|
||||
.dataLength = static_cast<uint32_t>(output.size()),
|
||||
.avDataId = dataId,
|
||||
};
|
||||
if (mPts) {
|
||||
mediaEvent.pts = mPts;
|
||||
mPts = 0;
|
||||
}
|
||||
int size = mFilterEvent.events.size();
|
||||
mFilterEvent.events.resize(size + 1);
|
||||
mFilterEvent.events[size].media(mediaEvent);
|
||||
|
||||
// Clear and log
|
||||
output.clear();
|
||||
mAvBufferCopyCount = 0;
|
||||
::close(av_fd);
|
||||
if (DEBUG_FILTER) {
|
||||
ALOGD("[Filter] av data length %d", mediaEvent.dataLength);
|
||||
}
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Result Filter::startRecordFilterHandler() {
|
||||
std::lock_guard<std::mutex> lock(mRecordFilterOutputLock);
|
||||
if (mRecordFilterOutput.empty()) {
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
if (mDvr == nullptr || !mDvr->writeRecordFMQ(mRecordFilterOutput)) {
|
||||
ALOGD("[Filter] dvr fails to write into record FMQ.");
|
||||
return Result::UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mRecordFilterOutput.clear();
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Result Filter::startPcrFilterHandler() {
|
||||
// TODO handle starting PCR filter
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Result Filter::startTemiFilterHandler() {
|
||||
// TODO handle starting TEMI filter
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
bool Filter::writeSectionsAndCreateEvent(vector<uint8_t> data) {
|
||||
// TODO check how many sections has been read
|
||||
ALOGD("[Filter] section handler");
|
||||
std::lock_guard<std::mutex> lock(mFilterEventLock);
|
||||
if (!writeDataToFilterMQ(data)) {
|
||||
return false;
|
||||
}
|
||||
int size = mFilterEvent.events.size();
|
||||
mFilterEvent.events.resize(size + 1);
|
||||
DemuxFilterSectionEvent secEvent;
|
||||
secEvent = {
|
||||
// temp dump meta data
|
||||
.tableId = 0,
|
||||
.version = 1,
|
||||
.sectionNum = 1,
|
||||
.dataLength = static_cast<uint16_t>(data.size()),
|
||||
};
|
||||
mFilterEvent.events[size].section(secEvent);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Filter::writeDataToFilterMQ(const std::vector<uint8_t>& data) {
|
||||
std::lock_guard<std::mutex> lock(mWriteLock);
|
||||
if (mFilterMQ->write(data.data(), data.size())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Filter::attachFilterToRecord(const sp<Dvr> dvr) {
|
||||
mDvr = dvr;
|
||||
}
|
||||
|
||||
void Filter::detachFilterFromRecord() {
|
||||
mDvr = nullptr;
|
||||
}
|
||||
|
||||
int Filter::createAvIonFd(int size) {
|
||||
// Create an ion fd and allocate an av fd mapped to a buffer to it.
|
||||
int ion_fd = ion_open();
|
||||
if (ion_fd == -1) {
|
||||
ALOGE("[Filter] Failed to open ion fd %d", errno);
|
||||
return -1;
|
||||
}
|
||||
int av_fd = -1;
|
||||
ion_alloc_fd(dup(ion_fd), size, 0 /*align*/, ION_HEAP_SYSTEM_MASK, 0 /*flags*/, &av_fd);
|
||||
if (av_fd == -1) {
|
||||
ALOGE("[Filter] Failed to create av fd %d", errno);
|
||||
return -1;
|
||||
}
|
||||
return av_fd;
|
||||
}
|
||||
|
||||
uint8_t* Filter::getIonBuffer(int fd, int size) {
|
||||
uint8_t* avBuf = static_cast<uint8_t*>(
|
||||
mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 /*offset*/));
|
||||
if (avBuf == MAP_FAILED) {
|
||||
ALOGE("[Filter] fail to allocate buffer %d", errno);
|
||||
return NULL;
|
||||
}
|
||||
return avBuf;
|
||||
}
|
||||
|
||||
native_handle_t* Filter::createNativeHandle(int fd) {
|
||||
// Create a native handle to pass the av fd via the callback event.
|
||||
native_handle_t* nativeHandle = native_handle_create(/*numFd*/ 1, 0);
|
||||
if (nativeHandle == NULL) {
|
||||
ALOGE("[Filter] Failed to create native_handle %d", errno);
|
||||
return NULL;
|
||||
}
|
||||
nativeHandle->data[0] = dup(fd);
|
||||
return nativeHandle;
|
||||
}
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
214
tv/tuner/1.1/default/Filter.h
Normal file
214
tv/tuner/1.1/default/Filter.h
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* 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_HARDWARE_TV_TUNER_V1_1_FILTER_H_
|
||||
#define ANDROID_HARDWARE_TV_TUNER_V1_1_FILTER_H_
|
||||
|
||||
#include <android/hardware/tv/tuner/1.1/IFilter.h>
|
||||
#include <fmq/MessageQueue.h>
|
||||
#include <inttypes.h>
|
||||
#include <ion/ion.h>
|
||||
#include <math.h>
|
||||
#include <set>
|
||||
#include "Demux.h"
|
||||
#include "Dvr.h"
|
||||
#include "Frontend.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace tv {
|
||||
namespace tuner {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::hardware::EventFlag;
|
||||
using ::android::hardware::kSynchronizedReadWrite;
|
||||
using ::android::hardware::MessageQueue;
|
||||
using ::android::hardware::MQDescriptorSync;
|
||||
|
||||
using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
|
||||
|
||||
class Demux;
|
||||
class Dvr;
|
||||
|
||||
class Filter : public V1_1::IFilter {
|
||||
public:
|
||||
Filter();
|
||||
|
||||
Filter(DemuxFilterType type, uint64_t filterId, uint32_t bufferSize,
|
||||
const sp<IFilterCallback>& cb, sp<Demux> demux);
|
||||
|
||||
~Filter();
|
||||
|
||||
virtual Return<void> getId64Bit(getId64Bit_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<void> getId(getId_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<Result> setDataSource(const sp<V1_0::IFilter>& filter) override;
|
||||
|
||||
virtual Return<void> getQueueDesc(getQueueDesc_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<Result> configure(const DemuxFilterSettings& settings) override;
|
||||
|
||||
virtual Return<Result> start() override;
|
||||
|
||||
virtual Return<Result> stop() override;
|
||||
|
||||
virtual Return<Result> flush() override;
|
||||
|
||||
virtual Return<Result> releaseAvHandle(const hidl_handle& avMemory, uint64_t avDataId) override;
|
||||
|
||||
virtual Return<Result> close() override;
|
||||
|
||||
/**
|
||||
* To create a FilterMQ and its Event Flag.
|
||||
*
|
||||
* Return false is any of the above processes fails.
|
||||
*/
|
||||
bool createFilterMQ();
|
||||
uint16_t getTpid();
|
||||
void updateFilterOutput(vector<uint8_t> data);
|
||||
void updateRecordOutput(vector<uint8_t> data);
|
||||
void updatePts(uint64_t pts);
|
||||
Result startFilterHandler();
|
||||
Result startRecordFilterHandler();
|
||||
void attachFilterToRecord(const sp<Dvr> dvr);
|
||||
void detachFilterFromRecord();
|
||||
void freeAvHandle();
|
||||
bool isMediaFilter() { return mIsMediaFilter; };
|
||||
bool isPcrFilter() { return mIsPcrFilter; };
|
||||
bool isRecordFilter() { return mIsRecordFilter; };
|
||||
|
||||
private:
|
||||
// Tuner service
|
||||
sp<Demux> mDemux;
|
||||
// Dvr reference once the filter is attached to any
|
||||
sp<Dvr> mDvr = nullptr;
|
||||
/**
|
||||
* Filter callbacks used on filter events or FMQ status
|
||||
*/
|
||||
sp<IFilterCallback> mCallback;
|
||||
|
||||
uint64_t mFilterId;
|
||||
uint32_t mBufferSize;
|
||||
DemuxFilterType mType;
|
||||
bool mIsMediaFilter = false;
|
||||
bool mIsPcrFilter = false;
|
||||
bool mIsRecordFilter = false;
|
||||
DemuxFilterSettings mFilterSettings;
|
||||
|
||||
uint16_t mTpid;
|
||||
sp<V1_0::IFilter> mDataSource;
|
||||
bool mIsDataSourceDemux = true;
|
||||
vector<uint8_t> mFilterOutput;
|
||||
vector<uint8_t> mRecordFilterOutput;
|
||||
uint64_t mPts = 0;
|
||||
unique_ptr<FilterMQ> mFilterMQ;
|
||||
bool mIsUsingFMQ = false;
|
||||
EventFlag* mFilterEventFlag;
|
||||
DemuxFilterEvent mFilterEvent;
|
||||
|
||||
// Thread handlers
|
||||
pthread_t mFilterThread;
|
||||
|
||||
// FMQ status local records
|
||||
DemuxFilterStatus mFilterStatus;
|
||||
/**
|
||||
* If a specific filter's writing loop is still running
|
||||
*/
|
||||
bool mFilterThreadRunning;
|
||||
bool mKeepFetchingDataFromFrontend;
|
||||
|
||||
/**
|
||||
* How many times a filter should write
|
||||
* TODO make this dynamic/random/can take as a parameter
|
||||
*/
|
||||
const uint16_t SECTION_WRITE_COUNT = 10;
|
||||
|
||||
bool DEBUG_FILTER = false;
|
||||
|
||||
/**
|
||||
* Filter handlers to handle the data filtering.
|
||||
* They are also responsible to write the filtered output into the filter FMQ
|
||||
* and update the filterEvent bound with the same filterId.
|
||||
*/
|
||||
Result startSectionFilterHandler();
|
||||
Result startPesFilterHandler();
|
||||
Result startTsFilterHandler();
|
||||
Result startMediaFilterHandler();
|
||||
Result startPcrFilterHandler();
|
||||
Result startTemiFilterHandler();
|
||||
Result startFilterLoop();
|
||||
|
||||
void deleteEventFlag();
|
||||
bool writeDataToFilterMQ(const std::vector<uint8_t>& data);
|
||||
bool readDataFromMQ();
|
||||
bool writeSectionsAndCreateEvent(vector<uint8_t> data);
|
||||
void maySendFilterStatusCallback();
|
||||
DemuxFilterStatus checkFilterStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
|
||||
uint32_t highThreshold, uint32_t lowThreshold);
|
||||
/**
|
||||
* A dispatcher to read and dispatch input data to all the started filters.
|
||||
* Each filter handler handles the data filtering/output writing/filterEvent updating.
|
||||
*/
|
||||
void startTsFilter(vector<uint8_t> data);
|
||||
bool startFilterDispatcher();
|
||||
static void* __threadLoopFilter(void* user);
|
||||
void filterThreadLoop();
|
||||
|
||||
int createAvIonFd(int size);
|
||||
uint8_t* getIonBuffer(int fd, int size);
|
||||
native_handle_t* createNativeHandle(int fd);
|
||||
Result createMediaFilterEventWithIon(vector<uint8_t> output);
|
||||
|
||||
/**
|
||||
* Lock to protect writes to the FMQs
|
||||
*/
|
||||
std::mutex mWriteLock;
|
||||
/**
|
||||
* Lock to protect writes to the filter event
|
||||
*/
|
||||
// TODO make each filter separate event lock
|
||||
std::mutex mFilterEventLock;
|
||||
/**
|
||||
* Lock to protect writes to the input status
|
||||
*/
|
||||
std::mutex mFilterStatusLock;
|
||||
std::mutex mFilterThreadLock;
|
||||
std::mutex mFilterOutputLock;
|
||||
std::mutex mRecordFilterOutputLock;
|
||||
|
||||
// temp handle single PES filter
|
||||
// TODO handle mulptiple Pes filters
|
||||
int mPesSizeLeft = 0;
|
||||
vector<uint8_t> mPesOutput;
|
||||
|
||||
// A map from data id to ion handle
|
||||
std::map<uint64_t, int> mDataId2Avfd;
|
||||
uint64_t mLastUsedDataId = 1;
|
||||
int mAvBufferCopyCount = 0;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_TV_TUNER_V1_1_FILTER_H_
|
||||
284
tv/tuner/1.1/default/Frontend.cpp
Normal file
284
tv/tuner/1.1/default/Frontend.cpp
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* 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 "android.hardware.tv.tuner@1.1-Frontend"
|
||||
|
||||
#include "Frontend.h"
|
||||
#include <android/hardware/tv/tuner/1.0/IFrontendCallback.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace tv {
|
||||
namespace tuner {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
Frontend::Frontend(FrontendType type, FrontendId id, sp<Tuner> tuner) {
|
||||
mType = type;
|
||||
mId = id;
|
||||
mTunerService = tuner;
|
||||
// Init callback to nullptr
|
||||
mCallback = nullptr;
|
||||
}
|
||||
|
||||
Frontend::~Frontend() {}
|
||||
|
||||
Return<Result> Frontend::close() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
// Reset callback
|
||||
mCallback = nullptr;
|
||||
mIsLocked = false;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Frontend::setCallback(const sp<IFrontendCallback>& callback) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
if (callback == nullptr) {
|
||||
ALOGW("[ WARN ] Set Frontend callback with nullptr");
|
||||
return Result::INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
mCallback = callback;
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Frontend::tune(const FrontendSettings& /* settings */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
if (mCallback == nullptr) {
|
||||
ALOGW("[ WARN ] Frontend callback is not set when tune");
|
||||
return Result::INVALID_STATE;
|
||||
}
|
||||
|
||||
mTunerService->frontendStartTune(mId);
|
||||
mCallback->onEvent(FrontendEventType::LOCKED);
|
||||
mIsLocked = true;
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Frontend::stopTune() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
mTunerService->frontendStopTune(mId);
|
||||
mIsLocked = false;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Frontend::scan(const FrontendSettings& settings, FrontendScanType type) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
if (mType == FrontendType::ATSC) {
|
||||
FrontendScanMessage msg;
|
||||
msg.isLocked(true);
|
||||
mCallback->onScanMessage(FrontendScanMessageType::LOCKED, msg);
|
||||
mIsLocked = true;
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
if (mType != FrontendType::DVBT) {
|
||||
return Result::UNAVAILABLE;
|
||||
}
|
||||
|
||||
FrontendScanMessage msg;
|
||||
|
||||
if (mIsLocked) {
|
||||
msg.isEnd(true);
|
||||
mCallback->onScanMessage(FrontendScanMessageType::END, msg);
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t frequency = settings.dvbt().frequency;
|
||||
if (type == FrontendScanType::SCAN_BLIND) {
|
||||
frequency += 100;
|
||||
}
|
||||
msg.frequencies({frequency});
|
||||
mCallback->onScanMessage(FrontendScanMessageType::FREQUENCY, msg);
|
||||
msg.isLocked(true);
|
||||
mCallback->onScanMessage(FrontendScanMessageType::LOCKED, msg);
|
||||
mIsLocked = true;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Frontend::stopScan() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
mIsLocked = false;
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<void> Frontend::getStatus(const hidl_vec<FrontendStatusType>& statusTypes,
|
||||
getStatus_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
vector<FrontendStatus> statuses;
|
||||
for (int i = 0; i < statusTypes.size(); i++) {
|
||||
FrontendStatusType type = statusTypes[i];
|
||||
FrontendStatus status;
|
||||
// assign randomly selected values for testing.
|
||||
switch (type) {
|
||||
case FrontendStatusType::DEMOD_LOCK: {
|
||||
status.isDemodLocked(true);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::SNR: {
|
||||
status.snr(221);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::BER: {
|
||||
status.ber(1);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::PER: {
|
||||
status.per(2);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::PRE_BER: {
|
||||
status.preBer(3);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::SIGNAL_QUALITY: {
|
||||
status.signalQuality(4);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::SIGNAL_STRENGTH: {
|
||||
status.signalStrength(5);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::SYMBOL_RATE: {
|
||||
status.symbolRate(6);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::FEC: {
|
||||
status.innerFec(FrontendInnerFec::FEC_2_9); // value = 1 << 7
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::MODULATION: {
|
||||
FrontendModulationStatus modulationStatus;
|
||||
modulationStatus.isdbt(FrontendIsdbtModulation::MOD_16QAM); // value = 1 << 3
|
||||
status.modulation(modulationStatus);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::SPECTRAL: {
|
||||
status.inversion(FrontendDvbcSpectralInversion::NORMAL);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::LNB_VOLTAGE: {
|
||||
status.lnbVoltage(LnbVoltage::VOLTAGE_5V);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::PLP_ID: {
|
||||
status.plpId(101); // type uint8_t
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::EWBS: {
|
||||
status.isEWBS(false);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::AGC: {
|
||||
status.agc(7);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::LNA: {
|
||||
status.isLnaOn(false);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::LAYER_ERROR: {
|
||||
vector<bool> v = {false, true, true};
|
||||
status.isLayerError(v);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::MER: {
|
||||
status.mer(8);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::FREQ_OFFSET: {
|
||||
status.freqOffset(9);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::HIERARCHY: {
|
||||
status.hierarchy(FrontendDvbtHierarchy::HIERARCHY_1_NATIVE);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::RF_LOCK: {
|
||||
status.isRfLocked(false);
|
||||
break;
|
||||
}
|
||||
case FrontendStatusType::ATSC3_PLP_INFO: {
|
||||
vector<FrontendStatusAtsc3PlpInfo> v;
|
||||
FrontendStatusAtsc3PlpInfo info1{
|
||||
.plpId = 3,
|
||||
.isLocked = false,
|
||||
.uec = 313,
|
||||
};
|
||||
FrontendStatusAtsc3PlpInfo info2{
|
||||
.plpId = 5,
|
||||
.isLocked = true,
|
||||
.uec = 515,
|
||||
};
|
||||
v.push_back(info1);
|
||||
v.push_back(info2);
|
||||
status.plpInfo(v);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
statuses.push_back(status);
|
||||
}
|
||||
_hidl_cb(Result::SUCCESS, statuses);
|
||||
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Frontend::setLna(bool /* bEnable */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Frontend::setLnb(uint32_t /* lnb */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
if (!supportsSatellite()) {
|
||||
return Result::INVALID_STATE;
|
||||
}
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
FrontendType Frontend::getFrontendType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
FrontendId Frontend::getFrontendId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
bool Frontend::supportsSatellite() {
|
||||
return mType == FrontendType::DVBS || mType == FrontendType::ISDBS ||
|
||||
mType == FrontendType::ISDBS3;
|
||||
}
|
||||
|
||||
bool Frontend::isLocked() {
|
||||
return mIsLocked;
|
||||
}
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
85
tv/tuner/1.1/default/Frontend.h
Normal file
85
tv/tuner/1.1/default/Frontend.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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_HARDWARE_TV_TUNER_V1_1_FRONTEND_H_
|
||||
#define ANDROID_HARDWARE_TV_TUNER_V1_1_FRONTEND_H_
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include "Tuner.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace tv {
|
||||
namespace tuner {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
class Tuner;
|
||||
|
||||
class Frontend : public IFrontend {
|
||||
public:
|
||||
Frontend(FrontendType type, FrontendId id, sp<Tuner> tuner);
|
||||
|
||||
virtual Return<Result> close() override;
|
||||
|
||||
virtual Return<Result> setCallback(const sp<IFrontendCallback>& callback) override;
|
||||
|
||||
virtual Return<Result> tune(const FrontendSettings& settings) override;
|
||||
|
||||
virtual Return<Result> stopTune() override;
|
||||
|
||||
virtual Return<Result> scan(const FrontendSettings& settings, FrontendScanType type) override;
|
||||
|
||||
virtual Return<Result> stopScan() override;
|
||||
|
||||
virtual Return<void> getStatus(const hidl_vec<FrontendStatusType>& statusTypes,
|
||||
getStatus_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<Result> setLna(bool bEnable) override;
|
||||
|
||||
virtual Return<Result> setLnb(uint32_t lnb) override;
|
||||
|
||||
FrontendType getFrontendType();
|
||||
|
||||
FrontendId getFrontendId();
|
||||
|
||||
string getSourceFile();
|
||||
|
||||
bool isLocked();
|
||||
|
||||
private:
|
||||
virtual ~Frontend();
|
||||
bool supportsSatellite();
|
||||
sp<IFrontendCallback> mCallback;
|
||||
sp<Tuner> mTunerService;
|
||||
FrontendType mType = FrontendType::UNDEFINED;
|
||||
FrontendId mId = 0;
|
||||
bool mIsLocked = false;
|
||||
|
||||
std::ifstream mFrontendData;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_TV_TUNER_V1_1_FRONTEND_H_
|
||||
81
tv/tuner/1.1/default/Lnb.cpp
Normal file
81
tv/tuner/1.1/default/Lnb.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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 "android.hardware.tv.tuner@1.1-Lnb"
|
||||
|
||||
#include "Lnb.h"
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace tv {
|
||||
namespace tuner {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
Lnb::Lnb() {}
|
||||
Lnb::Lnb(int id) {
|
||||
mId = id;
|
||||
}
|
||||
|
||||
Lnb::~Lnb() {}
|
||||
|
||||
Return<Result> Lnb::setCallback(const sp<ILnbCallback>& /* callback */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Lnb::setVoltage(LnbVoltage /* voltage */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Lnb::setTone(LnbTone /* tone */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Lnb::setSatellitePosition(LnbPosition /* position */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Lnb::sendDiseqcMessage(const hidl_vec<uint8_t>& /* diseqcMessage */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Lnb::close() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
int Lnb::getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
63
tv/tuner/1.1/default/Lnb.h
Normal file
63
tv/tuner/1.1/default/Lnb.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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_HARDWARE_TV_TUNER_V1_1_LNB_H_
|
||||
#define ANDROID_HARDWARE_TV_TUNER_V1_1_LNB_H_
|
||||
|
||||
#include <android/hardware/tv/tuner/1.0/ILnb.h>
|
||||
#include <android/hardware/tv/tuner/1.1/ITuner.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace tv {
|
||||
namespace tuner {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
class Lnb : public ILnb {
|
||||
public:
|
||||
Lnb();
|
||||
Lnb(int id);
|
||||
|
||||
virtual Return<Result> setCallback(const sp<ILnbCallback>& callback) override;
|
||||
|
||||
virtual Return<Result> setVoltage(LnbVoltage voltage) override;
|
||||
|
||||
virtual Return<Result> setTone(LnbTone tone) override;
|
||||
|
||||
virtual Return<Result> setSatellitePosition(LnbPosition position) override;
|
||||
|
||||
virtual Return<Result> sendDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) override;
|
||||
|
||||
virtual Return<Result> close() override;
|
||||
|
||||
int getId();
|
||||
|
||||
private:
|
||||
int mId;
|
||||
virtual ~Lnb();
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_TV_TUNER_V1_1_LNB_H_
|
||||
4
tv/tuner/1.1/default/OWNERS
Normal file
4
tv/tuner/1.1/default/OWNERS
Normal file
@@ -0,0 +1,4 @@
|
||||
nchalko@google.com
|
||||
amyjojo@google.com
|
||||
shubang@google.com
|
||||
quxiangfang@google.com
|
||||
87
tv/tuner/1.1/default/TimeFilter.cpp
Normal file
87
tv/tuner/1.1/default/TimeFilter.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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 "android.hardware.tv.tuner@1.1-TimeFilter"
|
||||
|
||||
#include "TimeFilter.h"
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace tv {
|
||||
namespace tuner {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
TimeFilter::TimeFilter() {}
|
||||
|
||||
TimeFilter::TimeFilter(sp<Demux> demux) {
|
||||
mDemux = demux;
|
||||
}
|
||||
|
||||
TimeFilter::~TimeFilter() {}
|
||||
|
||||
Return<Result> TimeFilter::setTimeStamp(uint64_t timeStamp) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
if (timeStamp == INVALID_TIME_STAMP) {
|
||||
return Result::INVALID_ARGUMENT;
|
||||
}
|
||||
mTimeStamp = timeStamp;
|
||||
mBeginTime = time(NULL);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> TimeFilter::clearTimeStamp() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
mTimeStamp = INVALID_TIME_STAMP;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<void> TimeFilter::getTimeStamp(getTimeStamp_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
if (mTimeStamp == INVALID_TIME_STAMP) {
|
||||
_hidl_cb(Result::INVALID_STATE, mTimeStamp);
|
||||
}
|
||||
|
||||
uint64_t currentTimeStamp = mTimeStamp + difftime(time(NULL), mBeginTime) * 900000;
|
||||
_hidl_cb(Result::SUCCESS, currentTimeStamp);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> TimeFilter::getSourceTime(getSourceTime_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
uint64_t time = 0;
|
||||
|
||||
_hidl_cb(Result::SUCCESS, time);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> TimeFilter::close() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
mTimeStamp = INVALID_TIME_STAMP;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
70
tv/tuner/1.1/default/TimeFilter.h
Normal file
70
tv/tuner/1.1/default/TimeFilter.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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_HARDWARE_TV_TUNER_V1_1_TIMEFILTER_H_
|
||||
#define ANDROID_HARDWARE_TV_TUNER_V1_1_TIMEFILTER_H_
|
||||
|
||||
#include <android/hardware/tv/tuner/1.0/ITimeFilter.h>
|
||||
#include "Demux.h"
|
||||
#include "time.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace tv {
|
||||
namespace tuner {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
|
||||
|
||||
#define INVALID_TIME_STAMP -1
|
||||
|
||||
class Demux;
|
||||
|
||||
class TimeFilter : public ITimeFilter {
|
||||
public:
|
||||
TimeFilter();
|
||||
|
||||
TimeFilter(sp<Demux> demux);
|
||||
|
||||
~TimeFilter();
|
||||
|
||||
virtual Return<Result> setTimeStamp(uint64_t timeStamp) override;
|
||||
|
||||
virtual Return<Result> clearTimeStamp() override;
|
||||
|
||||
virtual Return<void> getTimeStamp(getTimeStamp_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<void> getSourceTime(getSourceTime_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<Result> close() override;
|
||||
|
||||
private:
|
||||
sp<Demux> mDemux;
|
||||
uint64_t mTimeStamp = INVALID_TIME_STAMP;
|
||||
time_t mBeginTime;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_TV_TUNER_V1_1_TIMEFILTER_H_
|
||||
259
tv/tuner/1.1/default/Tuner.cpp
Normal file
259
tv/tuner/1.1/default/Tuner.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* 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 "android.hardware.tv.tuner@1.1-Tuner"
|
||||
|
||||
#include "Tuner.h"
|
||||
#include <utils/Log.h>
|
||||
#include "Demux.h"
|
||||
#include "Descrambler.h"
|
||||
#include "Frontend.h"
|
||||
#include "Lnb.h"
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace tv {
|
||||
namespace tuner {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
Tuner::Tuner() {
|
||||
// Static Frontends array to maintain local frontends information
|
||||
// Array index matches their FrontendId in the default impl
|
||||
mFrontendSize = 8;
|
||||
mFrontends.resize(mFrontendSize);
|
||||
mFrontends[0] = new Frontend(FrontendType::DVBT, 0, this);
|
||||
mFrontends[1] = new Frontend(FrontendType::ATSC, 1, this);
|
||||
mFrontends[2] = new Frontend(FrontendType::DVBC, 2, this);
|
||||
mFrontends[3] = new Frontend(FrontendType::DVBS, 3, this);
|
||||
mFrontends[4] = new Frontend(FrontendType::DVBT, 4, this);
|
||||
mFrontends[5] = new Frontend(FrontendType::ISDBT, 5, this);
|
||||
mFrontends[6] = new Frontend(FrontendType::ANALOG, 6, this);
|
||||
mFrontends[7] = new Frontend(FrontendType::ATSC, 7, this);
|
||||
|
||||
FrontendInfo::FrontendCapabilities caps;
|
||||
mFrontendCaps.resize(mFrontendSize);
|
||||
caps = FrontendInfo::FrontendCapabilities();
|
||||
caps.dvbtCaps(FrontendDvbtCapabilities());
|
||||
mFrontendCaps[0] = caps;
|
||||
|
||||
caps = FrontendInfo::FrontendCapabilities();
|
||||
caps.atscCaps(FrontendAtscCapabilities());
|
||||
mFrontendCaps[1] = caps;
|
||||
|
||||
caps = FrontendInfo::FrontendCapabilities();
|
||||
caps.dvbcCaps(FrontendDvbcCapabilities());
|
||||
mFrontendCaps[2] = caps;
|
||||
|
||||
caps = FrontendInfo::FrontendCapabilities();
|
||||
caps.dvbsCaps(FrontendDvbsCapabilities());
|
||||
mFrontendCaps[3] = caps;
|
||||
|
||||
caps = FrontendInfo::FrontendCapabilities();
|
||||
caps.dvbtCaps(FrontendDvbtCapabilities());
|
||||
mFrontendCaps[4] = caps;
|
||||
|
||||
caps = FrontendInfo::FrontendCapabilities();
|
||||
FrontendIsdbtCapabilities isdbtCaps{
|
||||
.modeCap = FrontendIsdbtMode::MODE_1 | FrontendIsdbtMode::MODE_2,
|
||||
.bandwidthCap = (unsigned int)FrontendIsdbtBandwidth::BANDWIDTH_6MHZ,
|
||||
.modulationCap = (unsigned int)FrontendIsdbtModulation::MOD_16QAM,
|
||||
// ISDBT shares coderate and guard interval with DVBT
|
||||
.coderateCap = FrontendDvbtCoderate::CODERATE_4_5 | FrontendDvbtCoderate::CODERATE_6_7,
|
||||
.guardIntervalCap = (unsigned int)FrontendDvbtGuardInterval::INTERVAL_1_128,
|
||||
};
|
||||
caps.isdbtCaps(isdbtCaps);
|
||||
mFrontendCaps[5] = caps;
|
||||
|
||||
caps = FrontendInfo::FrontendCapabilities();
|
||||
caps.analogCaps(FrontendAnalogCapabilities());
|
||||
mFrontendCaps[6] = caps;
|
||||
|
||||
caps = FrontendInfo::FrontendCapabilities();
|
||||
caps.atscCaps(FrontendAtscCapabilities());
|
||||
mFrontendCaps[7] = caps;
|
||||
|
||||
mLnbs.resize(2);
|
||||
mLnbs[0] = new Lnb(0);
|
||||
mLnbs[1] = new Lnb(1);
|
||||
}
|
||||
|
||||
Tuner::~Tuner() {}
|
||||
|
||||
Return<void> Tuner::getFrontendIds(getFrontendIds_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
vector<FrontendId> frontendIds;
|
||||
frontendIds.resize(mFrontendSize);
|
||||
for (int i = 0; i < mFrontendSize; i++) {
|
||||
frontendIds[i] = mFrontends[i]->getFrontendId();
|
||||
}
|
||||
|
||||
_hidl_cb(Result::SUCCESS, frontendIds);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Tuner::openFrontendById(uint32_t frontendId, openFrontendById_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
if (frontendId >= mFrontendSize || frontendId < 0) {
|
||||
ALOGW("[ WARN ] Frontend with id %d isn't available", frontendId);
|
||||
_hidl_cb(Result::UNAVAILABLE, nullptr);
|
||||
return Void();
|
||||
}
|
||||
|
||||
_hidl_cb(Result::SUCCESS, mFrontends[frontendId]);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Tuner::openDemux(openDemux_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
uint32_t demuxId = mLastUsedId + 1;
|
||||
mLastUsedId += 1;
|
||||
sp<Demux> demux = new Demux(demuxId, this);
|
||||
mDemuxes[demuxId] = demux;
|
||||
|
||||
_hidl_cb(Result::SUCCESS, demuxId, demux);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Tuner::getDemuxCaps(getDemuxCaps_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
DemuxCapabilities caps;
|
||||
|
||||
// IP filter can be an MMTP filter's data source.
|
||||
caps.linkCaps = {0x00, 0x00, 0x02, 0x00, 0x00};
|
||||
_hidl_cb(Result::SUCCESS, caps);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Tuner::openDescrambler(openDescrambler_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
sp<V1_0::IDescrambler> descrambler = new Descrambler();
|
||||
|
||||
_hidl_cb(Result::SUCCESS, descrambler);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Tuner::getFrontendInfo(FrontendId frontendId, getFrontendInfo_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
FrontendInfo info;
|
||||
if (frontendId >= mFrontendSize) {
|
||||
_hidl_cb(Result::INVALID_ARGUMENT, info);
|
||||
return Void();
|
||||
}
|
||||
|
||||
vector<FrontendStatusType> statusCaps = {
|
||||
FrontendStatusType::DEMOD_LOCK,
|
||||
FrontendStatusType::SNR,
|
||||
FrontendStatusType::FEC,
|
||||
FrontendStatusType::MODULATION,
|
||||
FrontendStatusType::PLP_ID,
|
||||
FrontendStatusType::LAYER_ERROR,
|
||||
FrontendStatusType::ATSC3_PLP_INFO,
|
||||
};
|
||||
// assign randomly selected values for testing.
|
||||
info = {
|
||||
.type = mFrontends[frontendId]->getFrontendType(),
|
||||
.minFrequency = 139,
|
||||
.maxFrequency = 1139,
|
||||
.minSymbolRate = 45,
|
||||
.maxSymbolRate = 1145,
|
||||
.acquireRange = 30,
|
||||
.exclusiveGroupId = 57,
|
||||
.statusCaps = statusCaps,
|
||||
.frontendCaps = mFrontendCaps[frontendId],
|
||||
};
|
||||
|
||||
_hidl_cb(Result::SUCCESS, info);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Tuner::getLnbIds(getLnbIds_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
vector<V1_0::LnbId> lnbIds;
|
||||
lnbIds.resize(mLnbs.size());
|
||||
for (int i = 0; i < lnbIds.size(); i++) {
|
||||
lnbIds[i] = mLnbs[i]->getId();
|
||||
}
|
||||
|
||||
_hidl_cb(Result::SUCCESS, lnbIds);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Tuner::openLnbById(V1_0::LnbId lnbId, openLnbById_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
if (lnbId >= mLnbs.size()) {
|
||||
_hidl_cb(Result::INVALID_ARGUMENT, nullptr);
|
||||
return Void();
|
||||
}
|
||||
|
||||
_hidl_cb(Result::SUCCESS, mLnbs[lnbId]);
|
||||
return Void();
|
||||
}
|
||||
|
||||
sp<Frontend> Tuner::getFrontendById(uint32_t frontendId) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return mFrontends[frontendId];
|
||||
}
|
||||
|
||||
Return<void> Tuner::openLnbByName(const hidl_string& /*lnbName*/, openLnbByName_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
sp<V1_0::ILnb> lnb = new Lnb();
|
||||
|
||||
_hidl_cb(Result::SUCCESS, 1234, lnb);
|
||||
return Void();
|
||||
}
|
||||
|
||||
void Tuner::setFrontendAsDemuxSource(uint32_t frontendId, uint32_t demuxId) {
|
||||
mFrontendToDemux[frontendId] = demuxId;
|
||||
if (mFrontends[frontendId] != nullptr && mFrontends[frontendId]->isLocked()) {
|
||||
mDemuxes[demuxId]->startFrontendInputLoop();
|
||||
}
|
||||
}
|
||||
|
||||
void Tuner::frontendStopTune(uint32_t frontendId) {
|
||||
map<uint32_t, uint32_t>::iterator it = mFrontendToDemux.find(frontendId);
|
||||
uint32_t demuxId;
|
||||
if (it != mFrontendToDemux.end()) {
|
||||
demuxId = it->second;
|
||||
mDemuxes[demuxId]->stopFrontendInput();
|
||||
}
|
||||
}
|
||||
|
||||
void Tuner::frontendStartTune(uint32_t frontendId) {
|
||||
map<uint32_t, uint32_t>::iterator it = mFrontendToDemux.find(frontendId);
|
||||
uint32_t demuxId;
|
||||
if (it != mFrontendToDemux.end()) {
|
||||
demuxId = it->second;
|
||||
mDemuxes[demuxId]->startFrontendInputLoop();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
94
tv/tuner/1.1/default/Tuner.h
Normal file
94
tv/tuner/1.1/default/Tuner.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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_HARDWARE_TV_TUNER_V1_1_TUNER_H_
|
||||
#define ANDROID_HARDWARE_TV_TUNER_V1_1_TUNER_H_
|
||||
|
||||
#include <android/hardware/tv/tuner/1.1/ITuner.h>
|
||||
#include <map>
|
||||
#include "Demux.h"
|
||||
#include "Frontend.h"
|
||||
#include "Lnb.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace tv {
|
||||
namespace tuner {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::hardware::tv::tuner::V1_1::ITuner;
|
||||
|
||||
class Frontend;
|
||||
class Demux;
|
||||
class Lnb;
|
||||
|
||||
class Tuner : public ITuner {
|
||||
public:
|
||||
Tuner();
|
||||
|
||||
virtual Return<void> getFrontendIds(getFrontendIds_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<void> openFrontendById(uint32_t frontendId,
|
||||
openFrontendById_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<void> openDemux(openDemux_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<void> getDemuxCaps(getDemuxCaps_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<void> openDescrambler(openDescrambler_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<void> getFrontendInfo(uint32_t frontendId, getFrontendInfo_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<void> getLnbIds(getLnbIds_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<void> openLnbById(uint32_t lnbId, openLnbById_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<void> openLnbByName(const hidl_string& lnbName,
|
||||
openLnbByName_cb _hidl_cb) override;
|
||||
|
||||
sp<Frontend> getFrontendById(uint32_t frontendId);
|
||||
|
||||
void setFrontendAsDemuxSource(uint32_t frontendId, uint32_t demuxId);
|
||||
|
||||
void frontendStartTune(uint32_t frontendId);
|
||||
void frontendStopTune(uint32_t frontendId);
|
||||
|
||||
private:
|
||||
virtual ~Tuner();
|
||||
// Static mFrontends array to maintain local frontends information
|
||||
vector<sp<Frontend>> mFrontends;
|
||||
vector<FrontendInfo::FrontendCapabilities> mFrontendCaps;
|
||||
std::map<uint32_t, uint32_t> mFrontendToDemux;
|
||||
std::map<uint32_t, sp<Demux>> mDemuxes;
|
||||
// To maintain how many Frontends we have
|
||||
int mFrontendSize;
|
||||
// The last used demux id. Initial value is -1.
|
||||
// First used id will be 0.
|
||||
uint32_t mLastUsedId = -1;
|
||||
vector<sp<Lnb>> mLnbs;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_TV_TUNER_V1_1_TUNER_H_
|
||||
@@ -0,0 +1,9 @@
|
||||
service vendor.tuner-hal-1-1 /vendor/bin/hw/android.hardware.tv.tuner@1.1-service-lazy
|
||||
interface android.hardware.tv.tuner@1.1::ITuner default
|
||||
oneshot
|
||||
disabled
|
||||
class hal
|
||||
user media
|
||||
group mediadrm drmrpc
|
||||
ioprio rt 4
|
||||
writepid /dev/cpuset/foreground/tasks
|
||||
@@ -0,0 +1,11 @@
|
||||
<manifest version="1.0" type="device">
|
||||
<hal format="hidl">
|
||||
<name>android.hardware.tv.tuner</name>
|
||||
<transport>hwbinder</transport>
|
||||
<version>1.1</version>
|
||||
<interface>
|
||||
<name>ITuner</name>
|
||||
<instance>default</instance>
|
||||
</interface>
|
||||
</hal>
|
||||
</manifest>
|
||||
@@ -0,0 +1,6 @@
|
||||
service vendor.tuner-hal-1-1 /vendor/bin/hw/android.hardware.tv.tuner@1.1-service
|
||||
class hal
|
||||
user media
|
||||
group mediadrm drmrpc
|
||||
ioprio rt 4
|
||||
writepid /dev/cpuset/foreground/tasks
|
||||
@@ -0,0 +1,11 @@
|
||||
<manifest version="1.0" type="device">
|
||||
<hal format="hidl">
|
||||
<name>android.hardware.tv.tuner</name>
|
||||
<transport>hwbinder</transport>
|
||||
<version>1.1</version>
|
||||
<interface>
|
||||
<name>ITuner</name>
|
||||
<instance>default</instance>
|
||||
</interface>
|
||||
</hal>
|
||||
</manifest>
|
||||
57
tv/tuner/1.1/default/service.cpp
Normal file
57
tv/tuner/1.1/default/service.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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_NDEBUG 0
|
||||
#ifdef LAZY_SERVICE
|
||||
#define LOG_TAG "android.hardware.tv.tuner@1.1-service-lazy"
|
||||
#else
|
||||
#define LOG_TAG "android.hardware.tv.tuner@1.1-service"
|
||||
#endif
|
||||
|
||||
#include <hidl/HidlTransportSupport.h>
|
||||
#include <hidl/LegacySupport.h>
|
||||
|
||||
#include "Tuner.h"
|
||||
|
||||
using android::hardware::configureRpcThreadpool;
|
||||
using android::hardware::joinRpcThreadpool;
|
||||
using android::hardware::LazyServiceRegistrar;
|
||||
using android::hardware::tv::tuner::V1_0::implementation::Tuner;
|
||||
using android::hardware::tv::tuner::V1_1::ITuner;
|
||||
|
||||
#ifdef LAZY_SERVICE
|
||||
const bool kLazyService = true;
|
||||
#else
|
||||
const bool kLazyService = false;
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
configureRpcThreadpool(8, true /* callerWillJoin */);
|
||||
|
||||
// Setup hwbinder service
|
||||
android::sp<ITuner> service = new Tuner();
|
||||
android::status_t status;
|
||||
if (kLazyService) {
|
||||
auto serviceRegistrar = LazyServiceRegistrar::getInstance();
|
||||
status = serviceRegistrar.registerService(service);
|
||||
} else {
|
||||
status = service->registerAsService();
|
||||
}
|
||||
LOG_ALWAYS_FATAL_IF(status != android::OK, "Error while registering tuner service: %d", status);
|
||||
|
||||
joinRpcThreadpool();
|
||||
return 0;
|
||||
}
|
||||
4
tv/tuner/1.1/vts/OWNERS
Normal file
4
tv/tuner/1.1/vts/OWNERS
Normal file
@@ -0,0 +1,4 @@
|
||||
nchalko@google.com
|
||||
amyjojo@google.com
|
||||
shubang@google.com
|
||||
quxiangfang@google.com
|
||||
48
tv/tuner/1.1/vts/functional/Android.bp
Normal file
48
tv/tuner/1.1/vts/functional/Android.bp
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
cc_test {
|
||||
name: "VtsHalTvTunerV1_1TargetTest",
|
||||
defaults: ["VtsHalTargetTestDefaults"],
|
||||
srcs: [
|
||||
"VtsHalTvTunerV1_1TargetTest.cpp",
|
||||
"FrontendTests.cpp",
|
||||
"DemuxTests.cpp",
|
||||
"FilterTests.cpp",
|
||||
],
|
||||
static_libs: [
|
||||
"android.hardware.cas@1.0",
|
||||
"android.hardware.cas@1.1",
|
||||
"android.hardware.cas@1.2",
|
||||
"android.hardware.tv.tuner@1.0",
|
||||
"android.hardware.tv.tuner@1.1",
|
||||
"android.hidl.allocator@1.0",
|
||||
"android.hidl.memory@1.0",
|
||||
"libhidlallocatorutils",
|
||||
"libhidlmemory",
|
||||
"libcutils",
|
||||
"libfmq",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbinder",
|
||||
],
|
||||
test_suites: [
|
||||
"general-tests",
|
||||
"vts",
|
||||
],
|
||||
|
||||
require_root: true,
|
||||
}
|
||||
41
tv/tuner/1.1/vts/functional/DemuxTests.cpp
Normal file
41
tv/tuner/1.1/vts/functional/DemuxTests.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "DemuxTests.h"
|
||||
|
||||
AssertionResult DemuxTests::openDemux(sp<IDemux>& demux, uint32_t& demuxId) {
|
||||
Result status;
|
||||
mService->openDemux([&](Result result, uint32_t id, const sp<IDemux>& demuxSp) {
|
||||
mDemux = demuxSp;
|
||||
demux = demuxSp;
|
||||
demuxId = id;
|
||||
status = result;
|
||||
});
|
||||
return AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
|
||||
AssertionResult DemuxTests::setDemuxFrontendDataSource(uint32_t frontendId) {
|
||||
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
|
||||
auto status = mDemux->setFrontendDataSource(frontendId);
|
||||
return AssertionResult(status.isOk());
|
||||
}
|
||||
|
||||
AssertionResult DemuxTests::closeDemux() {
|
||||
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
|
||||
auto status = mDemux->close();
|
||||
mDemux = nullptr;
|
||||
return AssertionResult(status.isOk());
|
||||
}
|
||||
56
tv/tuner/1.1/vts/functional/DemuxTests.h
Normal file
56
tv/tuner/1.1/vts/functional/DemuxTests.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android/hardware/tv/tuner/1.0/IDemux.h>
|
||||
#include <android/hardware/tv/tuner/1.0/types.h>
|
||||
#include <android/hardware/tv/tuner/1.1/IFilter.h>
|
||||
#include <android/hardware/tv/tuner/1.1/ITuner.h>
|
||||
#include <binder/MemoryDealer.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <hidl/ServiceManagement.h>
|
||||
#include <hidl/Status.h>
|
||||
#include <hidlmemory/FrameworkUtils.h>
|
||||
#include <utils/Condition.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <map>
|
||||
|
||||
using android::sp;
|
||||
using android::hardware::Return;
|
||||
using android::hardware::Void;
|
||||
using android::hardware::tv::tuner::V1_0::IDemux;
|
||||
using android::hardware::tv::tuner::V1_0::IFilter;
|
||||
using android::hardware::tv::tuner::V1_0::Result;
|
||||
using android::hardware::tv::tuner::V1_1::ITuner;
|
||||
|
||||
using ::testing::AssertionResult;
|
||||
|
||||
class DemuxTests {
|
||||
public:
|
||||
void setService(sp<ITuner> tuner) { mService = tuner; }
|
||||
|
||||
AssertionResult openDemux(sp<IDemux>& demux, uint32_t& demuxId);
|
||||
AssertionResult setDemuxFrontendDataSource(uint32_t frontendId);
|
||||
AssertionResult closeDemux();
|
||||
|
||||
protected:
|
||||
static AssertionResult failure() { return ::testing::AssertionFailure(); }
|
||||
|
||||
static AssertionResult success() { return ::testing::AssertionSuccess(); }
|
||||
|
||||
sp<ITuner> mService;
|
||||
sp<IDemux> mDemux;
|
||||
};
|
||||
111
tv/tuner/1.1/vts/functional/FilterTests.cpp
Normal file
111
tv/tuner/1.1/vts/functional/FilterTests.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "FilterTests.h"
|
||||
|
||||
AssertionResult FilterTests::openFilterInDemux(DemuxFilterType type, uint32_t bufferSize) {
|
||||
Result status;
|
||||
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
|
||||
|
||||
// Create demux callback
|
||||
mFilterCallback = new FilterCallback();
|
||||
|
||||
// Add filter to the local demux
|
||||
mDemux->openFilter(type, bufferSize, mFilterCallback,
|
||||
[&](Result result, const sp<IFilter>& filter) {
|
||||
mFilter = filter;
|
||||
status = result;
|
||||
});
|
||||
|
||||
return AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
|
||||
AssertionResult FilterTests::getNewlyOpenedFilterId_64bit(uint64_t& filterId) {
|
||||
Result status;
|
||||
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
|
||||
EXPECT_TRUE(mFilter) << "Test with openFilterInDemux first.";
|
||||
EXPECT_TRUE(mFilterCallback) << "Test with openFilterInDemux first.";
|
||||
|
||||
sp<android::hardware::tv::tuner::V1_1::IFilter> filter_v1_1 =
|
||||
android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilter);
|
||||
if (filter_v1_1 != NULL) {
|
||||
filter_v1_1->getId64Bit([&](Result result, uint64_t filterId) {
|
||||
mFilterId = filterId;
|
||||
status = result;
|
||||
});
|
||||
} else {
|
||||
ALOGW("[vts] Can't cast IFilter into v1_1.");
|
||||
return failure();
|
||||
}
|
||||
|
||||
if (status == Result::SUCCESS) {
|
||||
mUsedFilterIds.insert(mUsedFilterIds.end(), mFilterId);
|
||||
mFilters[mFilterId] = mFilter;
|
||||
mFilterCallbacks[mFilterId] = mFilterCallback;
|
||||
filterId = mFilterId;
|
||||
}
|
||||
|
||||
return AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
|
||||
AssertionResult FilterTests::configFilter(DemuxFilterSettings setting, uint64_t filterId) {
|
||||
Result status;
|
||||
EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
|
||||
status = mFilters[filterId]->configure(setting);
|
||||
|
||||
return AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
|
||||
AssertionResult FilterTests::getFilterMQDescriptor(uint64_t filterId) {
|
||||
Result status;
|
||||
EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
|
||||
EXPECT_TRUE(mFilterCallbacks[filterId]) << "Test with getNewlyOpenedFilterId first.";
|
||||
|
||||
mFilter->getQueueDesc([&](Result result, const MQDesc& filterMQDesc) {
|
||||
mFilterMQDescriptor = filterMQDesc;
|
||||
status = result;
|
||||
});
|
||||
|
||||
return AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
|
||||
AssertionResult FilterTests::startFilter(uint64_t filterId) {
|
||||
EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
|
||||
Result status = mFilters[filterId]->start();
|
||||
return AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
|
||||
AssertionResult FilterTests::stopFilter(uint64_t filterId) {
|
||||
EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
|
||||
Result status = mFilters[filterId]->stop();
|
||||
return AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
|
||||
AssertionResult FilterTests::closeFilter(uint64_t filterId) {
|
||||
EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
|
||||
Result status = mFilters[filterId]->close();
|
||||
if (status == Result::SUCCESS) {
|
||||
for (int i = 0; i < mUsedFilterIds.size(); i++) {
|
||||
if (mUsedFilterIds[i] == filterId) {
|
||||
mUsedFilterIds.erase(mUsedFilterIds.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mFilterCallbacks.erase(filterId);
|
||||
mFilters.erase(filterId);
|
||||
}
|
||||
return AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
168
tv/tuner/1.1/vts/functional/FilterTests.h
Normal file
168
tv/tuner/1.1/vts/functional/FilterTests.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android/hardware/tv/tuner/1.0/IDemux.h>
|
||||
#include <android/hardware/tv/tuner/1.0/IFilter.h>
|
||||
#include <android/hardware/tv/tuner/1.0/IFilterCallback.h>
|
||||
#include <android/hardware/tv/tuner/1.0/types.h>
|
||||
#include <android/hardware/tv/tuner/1.1/IFilter.h>
|
||||
#include <android/hardware/tv/tuner/1.1/ITuner.h>
|
||||
#include <fmq/MessageQueue.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <hidl/HidlSupport.h>
|
||||
#include <hidl/HidlTransportSupport.h>
|
||||
#include <hidl/Status.h>
|
||||
#include <inttypes.h>
|
||||
#include <utils/Condition.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <map>
|
||||
|
||||
using android::Condition;
|
||||
using android::Mutex;
|
||||
using android::sp;
|
||||
using android::hardware::EventFlag;
|
||||
using android::hardware::hidl_handle;
|
||||
using android::hardware::hidl_string;
|
||||
using android::hardware::hidl_vec;
|
||||
using android::hardware::kSynchronizedReadWrite;
|
||||
using android::hardware::MessageQueue;
|
||||
using android::hardware::MQDescriptorSync;
|
||||
using android::hardware::Return;
|
||||
using android::hardware::Void;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterType;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
|
||||
using android::hardware::tv::tuner::V1_0::IDemux;
|
||||
using android::hardware::tv::tuner::V1_0::IFilter;
|
||||
using android::hardware::tv::tuner::V1_0::IFilterCallback;
|
||||
using android::hardware::tv::tuner::V1_0::Result;
|
||||
using android::hardware::tv::tuner::V1_1::ITuner;
|
||||
|
||||
using ::testing::AssertionResult;
|
||||
|
||||
using namespace std;
|
||||
|
||||
enum FilterEventType : uint8_t {
|
||||
UNDEFINED,
|
||||
SECTION,
|
||||
MEDIA,
|
||||
PES,
|
||||
RECORD,
|
||||
MMTPRECORD,
|
||||
DOWNLOAD,
|
||||
TEMI,
|
||||
};
|
||||
|
||||
using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
|
||||
using MQDesc = MQDescriptorSync<uint8_t>;
|
||||
|
||||
#define WAIT_TIMEOUT 3000000000
|
||||
|
||||
class FilterCallback : public IFilterCallback {
|
||||
public:
|
||||
virtual Return<void> onFilterEvent(const DemuxFilterEvent& /*filterEvent*/) override {
|
||||
return Void();
|
||||
}
|
||||
|
||||
virtual Return<void> onFilterStatus(const DemuxFilterStatus /*status*/) override {
|
||||
return Void();
|
||||
}
|
||||
};
|
||||
|
||||
class FilterTests {
|
||||
public:
|
||||
void setService(sp<ITuner> tuner) { mService = tuner; }
|
||||
void setDemux(sp<IDemux> demux) { mDemux = demux; }
|
||||
sp<IFilter> getFilterById(uint64_t filterId) { return mFilters[filterId]; }
|
||||
|
||||
std::map<uint64_t, sp<FilterCallback>> getFilterCallbacks() { return mFilterCallbacks; }
|
||||
|
||||
AssertionResult openFilterInDemux(DemuxFilterType type, uint32_t bufferSize);
|
||||
AssertionResult getNewlyOpenedFilterId_64bit(uint64_t& filterId);
|
||||
AssertionResult configFilter(DemuxFilterSettings setting, uint64_t filterId);
|
||||
AssertionResult getFilterMQDescriptor(uint64_t filterId);
|
||||
AssertionResult startFilter(uint64_t filterId);
|
||||
AssertionResult stopFilter(uint64_t filterId);
|
||||
AssertionResult closeFilter(uint64_t filterId);
|
||||
|
||||
FilterEventType getFilterEventType(DemuxFilterType type) {
|
||||
FilterEventType eventType = FilterEventType::UNDEFINED;
|
||||
switch (type.mainType) {
|
||||
case DemuxFilterMainType::TS:
|
||||
switch (type.subType.tsFilterType()) {
|
||||
case DemuxTsFilterType::UNDEFINED:
|
||||
break;
|
||||
case DemuxTsFilterType::SECTION:
|
||||
eventType = FilterEventType::SECTION;
|
||||
break;
|
||||
case DemuxTsFilterType::PES:
|
||||
eventType = FilterEventType::PES;
|
||||
break;
|
||||
case DemuxTsFilterType::TS:
|
||||
break;
|
||||
case DemuxTsFilterType::AUDIO:
|
||||
case DemuxTsFilterType::VIDEO:
|
||||
eventType = FilterEventType::MEDIA;
|
||||
break;
|
||||
case DemuxTsFilterType::PCR:
|
||||
break;
|
||||
case DemuxTsFilterType::RECORD:
|
||||
eventType = FilterEventType::RECORD;
|
||||
break;
|
||||
case DemuxTsFilterType::TEMI:
|
||||
eventType = FilterEventType::TEMI;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case DemuxFilterMainType::MMTP:
|
||||
/*mmtpSettings*/
|
||||
break;
|
||||
case DemuxFilterMainType::IP:
|
||||
/*ipSettings*/
|
||||
break;
|
||||
case DemuxFilterMainType::TLV:
|
||||
/*tlvSettings*/
|
||||
break;
|
||||
case DemuxFilterMainType::ALP:
|
||||
/*alpSettings*/
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return eventType;
|
||||
}
|
||||
|
||||
protected:
|
||||
static AssertionResult failure() { return ::testing::AssertionFailure(); }
|
||||
|
||||
static AssertionResult success() { return ::testing::AssertionSuccess(); }
|
||||
|
||||
sp<ITuner> mService;
|
||||
sp<IFilter> mFilter;
|
||||
sp<IDemux> mDemux;
|
||||
std::map<uint64_t, sp<IFilter>> mFilters;
|
||||
std::map<uint64_t, sp<FilterCallback>> mFilterCallbacks;
|
||||
|
||||
sp<FilterCallback> mFilterCallback;
|
||||
MQDesc mFilterMQDescriptor;
|
||||
vector<uint64_t> mUsedFilterIds;
|
||||
|
||||
uint64_t mFilterId = -1;
|
||||
};
|
||||
83
tv/tuner/1.1/vts/functional/FrontendTests.cpp
Normal file
83
tv/tuner/1.1/vts/functional/FrontendTests.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "FrontendTests.h"
|
||||
|
||||
Return<void> FrontendCallback::onEvent(FrontendEventType /*frontendEventType*/) {
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> FrontendCallback::onScanMessage(FrontendScanMessageType /*type*/,
|
||||
const FrontendScanMessage& /*message*/) {
|
||||
return Void();
|
||||
}
|
||||
|
||||
AssertionResult FrontendTests::getFrontendIds() {
|
||||
Result status;
|
||||
mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
|
||||
status = result;
|
||||
mFeIds = frontendIds;
|
||||
});
|
||||
return AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
|
||||
AssertionResult FrontendTests::getFrontendInfo(uint32_t frontendId) {
|
||||
Result status;
|
||||
mService->getFrontendInfo(frontendId, [&](Result result, const FrontendInfo& frontendInfo) {
|
||||
mFrontendInfo = frontendInfo;
|
||||
status = result;
|
||||
});
|
||||
return AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
|
||||
AssertionResult FrontendTests::openFrontendById(uint32_t frontendId) {
|
||||
Result status;
|
||||
mService->openFrontendById(frontendId, [&](Result result, const sp<IFrontend>& frontend) {
|
||||
mFrontend = frontend;
|
||||
status = result;
|
||||
});
|
||||
return AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
|
||||
AssertionResult FrontendTests::setFrontendCallback() {
|
||||
EXPECT_TRUE(mFrontend) << "Test with openFrontendById first.";
|
||||
mFrontendCallback = new FrontendCallback();
|
||||
auto callbackStatus = mFrontend->setCallback(mFrontendCallback);
|
||||
return AssertionResult(callbackStatus.isOk());
|
||||
}
|
||||
|
||||
AssertionResult FrontendTests::closeFrontend() {
|
||||
EXPECT_TRUE(mFrontend) << "Test with openFrontendById first.";
|
||||
Result status;
|
||||
status = mFrontend->close();
|
||||
mFrontend = nullptr;
|
||||
mFrontendCallback = nullptr;
|
||||
return AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
|
||||
void FrontendTests::getFrontendIdByType(FrontendType feType, uint32_t& feId) {
|
||||
ASSERT_TRUE(getFrontendIds());
|
||||
ASSERT_TRUE(mFeIds.size() > 0);
|
||||
for (size_t i = 0; i < mFeIds.size(); i++) {
|
||||
ASSERT_TRUE(getFrontendInfo(mFeIds[i]));
|
||||
if (mFrontendInfo.type != feType) {
|
||||
continue;
|
||||
}
|
||||
feId = mFeIds[i];
|
||||
return;
|
||||
}
|
||||
feId = INVALID_ID;
|
||||
}
|
||||
96
tv/tuner/1.1/vts/functional/FrontendTests.h
Normal file
96
tv/tuner/1.1/vts/functional/FrontendTests.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android/hardware/tv/tuner/1.0/IFrontend.h>
|
||||
#include <android/hardware/tv/tuner/1.0/IFrontendCallback.h>
|
||||
#include <android/hardware/tv/tuner/1.0/types.h>
|
||||
#include <android/hardware/tv/tuner/1.1/ITuner.h>
|
||||
#include <binder/MemoryDealer.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <hidl/GtestPrinter.h>
|
||||
#include <hidl/HidlSupport.h>
|
||||
#include <hidl/HidlTransportSupport.h>
|
||||
#include <hidl/ServiceManagement.h>
|
||||
#include <hidl/Status.h>
|
||||
#include <hidlmemory/FrameworkUtils.h>
|
||||
#include <utils/Condition.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <map>
|
||||
|
||||
#include "VtsHalTvTunerV1_1TestConfigurations.h"
|
||||
|
||||
#define WAIT_TIMEOUT 3000000000
|
||||
#define INVALID_ID -1
|
||||
|
||||
using android::Condition;
|
||||
using android::IMemory;
|
||||
using android::IMemoryHeap;
|
||||
using android::MemoryDealer;
|
||||
using android::Mutex;
|
||||
using android::sp;
|
||||
using android::hardware::fromHeap;
|
||||
using android::hardware::hidl_vec;
|
||||
using android::hardware::Return;
|
||||
using android::hardware::Void;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendEventType;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendId;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendInfo;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendScanMessage;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
|
||||
using android::hardware::tv::tuner::V1_0::IFrontend;
|
||||
using android::hardware::tv::tuner::V1_0::IFrontendCallback;
|
||||
using android::hardware::tv::tuner::V1_0::Result;
|
||||
using android::hardware::tv::tuner::V1_1::ITuner;
|
||||
|
||||
using ::testing::AssertionResult;
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define INVALID_ID -1
|
||||
|
||||
class FrontendCallback : public IFrontendCallback {
|
||||
public:
|
||||
virtual Return<void> onEvent(FrontendEventType frontendEventType) override;
|
||||
virtual Return<void> onScanMessage(FrontendScanMessageType type,
|
||||
const FrontendScanMessage& message) override;
|
||||
};
|
||||
|
||||
class FrontendTests {
|
||||
public:
|
||||
sp<ITuner> mService;
|
||||
|
||||
void setService(sp<ITuner> tuner) { mService = tuner; }
|
||||
|
||||
AssertionResult getFrontendIds();
|
||||
AssertionResult getFrontendInfo(uint32_t frontendId);
|
||||
AssertionResult openFrontendById(uint32_t frontendId);
|
||||
AssertionResult setFrontendCallback();
|
||||
AssertionResult closeFrontend();
|
||||
|
||||
void getFrontendIdByType(FrontendType feType, uint32_t& feId);
|
||||
|
||||
protected:
|
||||
static AssertionResult failure() { return ::testing::AssertionFailure(); }
|
||||
static AssertionResult success() { return ::testing::AssertionSuccess(); }
|
||||
|
||||
sp<IFrontend> mFrontend;
|
||||
FrontendInfo mFrontendInfo;
|
||||
sp<FrontendCallback> mFrontendCallback;
|
||||
hidl_vec<FrontendId> mFeIds;
|
||||
|
||||
bool mIsSoftwareFe = false;
|
||||
};
|
||||
58
tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
Normal file
58
tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "VtsHalTvTunerV1_1TargetTest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void TunerFilterHidlTest::configSingleFilterInDemuxTest(FilterConfig filterConf,
|
||||
FrontendConfig frontendConf) {
|
||||
uint32_t feId;
|
||||
uint32_t demuxId;
|
||||
sp<IDemux> demux;
|
||||
uint64_t filterId;
|
||||
|
||||
mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
|
||||
ASSERT_TRUE(feId != INVALID_ID);
|
||||
ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
|
||||
ASSERT_TRUE(mFrontendTests.setFrontendCallback());
|
||||
ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
|
||||
ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
|
||||
mFilterTests.setDemux(demux);
|
||||
ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
|
||||
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId));
|
||||
ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
|
||||
ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
|
||||
ASSERT_TRUE(mFilterTests.startFilter(filterId));
|
||||
ASSERT_TRUE(mFilterTests.stopFilter(filterId));
|
||||
ASSERT_TRUE(mFilterTests.closeFilter(filterId));
|
||||
ASSERT_TRUE(mDemuxTests.closeDemux());
|
||||
ASSERT_TRUE(mFrontendTests.closeFrontend());
|
||||
}
|
||||
|
||||
TEST_P(TunerFilterHidlTest, StartFilterInDemux) {
|
||||
description("Open and start a filter in Demux.");
|
||||
// TODO use parameterized tests
|
||||
configSingleFilterInDemuxTest(filterArray[TS_VIDEO0], frontendArray[DVBT]);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
PerInstance, TunerFilterHidlTest,
|
||||
testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)),
|
||||
android::hardware::PrintInstanceNameToString);
|
||||
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerFilterHidlTest);
|
||||
} // namespace
|
||||
52
tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h
Normal file
52
tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "DemuxTests.h"
|
||||
#include "FilterTests.h"
|
||||
#include "FrontendTests.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void initConfiguration() {
|
||||
initFrontendConfig();
|
||||
initFilterConfig();
|
||||
}
|
||||
|
||||
class TunerFilterHidlTest : public testing::TestWithParam<std::string> {
|
||||
public:
|
||||
virtual void SetUp() override {
|
||||
mService = ITuner::getService(GetParam());
|
||||
ASSERT_NE(mService, nullptr);
|
||||
initConfiguration();
|
||||
|
||||
mFrontendTests.setService(mService);
|
||||
mDemuxTests.setService(mService);
|
||||
mFilterTests.setService(mService);
|
||||
}
|
||||
|
||||
protected:
|
||||
static void description(const std::string& description) {
|
||||
RecordProperty("description", description);
|
||||
}
|
||||
|
||||
void configSingleFilterInDemuxTest(FilterConfig filterConf, FrontendConfig frontendConf);
|
||||
|
||||
sp<ITuner> mService;
|
||||
FrontendTests mFrontendTests;
|
||||
DemuxTests mDemuxTests;
|
||||
FilterTests mFilterTests;
|
||||
};
|
||||
} // namespace
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <android/hardware/tv/tuner/1.0/types.h>
|
||||
#include <binder/MemoryDealer.h>
|
||||
#include <hidl/HidlSupport.h>
|
||||
#include <hidl/HidlTransportSupport.h>
|
||||
#include <hidl/Status.h>
|
||||
#include <hidlmemory/FrameworkUtils.h>
|
||||
|
||||
using android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterType;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxIpFilterType;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxRecordScIndexType;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendDvbtCoderate;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendDvbtConstellation;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendDvbtSettings;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendDvbtStandard;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendDvbtTransmissionMode;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendSettings;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendStatus;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendStatusType;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendType;
|
||||
|
||||
using namespace std;
|
||||
|
||||
const uint32_t FMQ_SIZE_1M = 0x100000;
|
||||
const uint32_t FMQ_SIZE_4M = 0x400000;
|
||||
const uint32_t FMQ_SIZE_16M = 0x1000000;
|
||||
|
||||
typedef enum {
|
||||
TS_VIDEO0,
|
||||
TS_VIDEO1,
|
||||
TS_AUDIO0,
|
||||
TS_AUDIO1,
|
||||
TS_PES0,
|
||||
TS_PCR0,
|
||||
TS_SECTION0,
|
||||
TS_TS0,
|
||||
TS_RECORD0,
|
||||
FILTER_MAX,
|
||||
} Filter;
|
||||
|
||||
typedef enum {
|
||||
DVBT,
|
||||
DVBS,
|
||||
FRONTEND_MAX,
|
||||
} Frontend;
|
||||
|
||||
struct FilterConfig {
|
||||
uint32_t bufferSize;
|
||||
DemuxFilterType type;
|
||||
DemuxFilterSettings settings;
|
||||
|
||||
bool operator<(const FilterConfig& /*c*/) const { return false; }
|
||||
};
|
||||
|
||||
struct FrontendConfig {
|
||||
bool isSoftwareFe;
|
||||
FrontendType type;
|
||||
FrontendSettings settings;
|
||||
vector<FrontendStatusType> tuneStatusTypes;
|
||||
vector<FrontendStatus> expectTuneStatuses;
|
||||
};
|
||||
|
||||
static FrontendConfig frontendArray[FILTER_MAX];
|
||||
static FilterConfig filterArray[FILTER_MAX];
|
||||
|
||||
/** Configuration array for the frontend tune test */
|
||||
inline void initFrontendConfig() {
|
||||
FrontendDvbtSettings dvbtSettings{
|
||||
.frequency = 578000,
|
||||
.transmissionMode = FrontendDvbtTransmissionMode::AUTO,
|
||||
.bandwidth = FrontendDvbtBandwidth::BANDWIDTH_8MHZ,
|
||||
.constellation = FrontendDvbtConstellation::AUTO,
|
||||
.hierarchy = FrontendDvbtHierarchy::AUTO,
|
||||
.hpCoderate = FrontendDvbtCoderate::AUTO,
|
||||
.lpCoderate = FrontendDvbtCoderate::AUTO,
|
||||
.guardInterval = FrontendDvbtGuardInterval::AUTO,
|
||||
.isHighPriority = true,
|
||||
.standard = FrontendDvbtStandard::T,
|
||||
};
|
||||
frontendArray[DVBT].type = FrontendType::DVBT, frontendArray[DVBT].settings.dvbt(dvbtSettings);
|
||||
vector<FrontendStatusType> types;
|
||||
types.push_back(FrontendStatusType::DEMOD_LOCK);
|
||||
FrontendStatus status;
|
||||
status.isDemodLocked(true);
|
||||
vector<FrontendStatus> statuses;
|
||||
statuses.push_back(status);
|
||||
frontendArray[DVBT].tuneStatusTypes = types;
|
||||
frontendArray[DVBT].expectTuneStatuses = statuses;
|
||||
frontendArray[DVBT].isSoftwareFe = true;
|
||||
frontendArray[DVBS].type = FrontendType::DVBS;
|
||||
frontendArray[DVBS].isSoftwareFe = true;
|
||||
};
|
||||
|
||||
/** Configuration array for the filter test */
|
||||
inline void initFilterConfig() {
|
||||
// TS VIDEO filter setting for default implementation testing
|
||||
filterArray[TS_VIDEO0].type.mainType = DemuxFilterMainType::TS;
|
||||
filterArray[TS_VIDEO0].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
|
||||
filterArray[TS_VIDEO0].bufferSize = FMQ_SIZE_16M;
|
||||
filterArray[TS_VIDEO0].settings.ts().tpid = 256;
|
||||
filterArray[TS_VIDEO0].settings.ts().filterSettings.av({.isPassthrough = false});
|
||||
filterArray[TS_VIDEO1].type.mainType = DemuxFilterMainType::TS;
|
||||
filterArray[TS_VIDEO1].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
|
||||
filterArray[TS_VIDEO1].bufferSize = FMQ_SIZE_16M;
|
||||
filterArray[TS_VIDEO1].settings.ts().tpid = 256;
|
||||
filterArray[TS_VIDEO1].settings.ts().filterSettings.av({.isPassthrough = false});
|
||||
// TS AUDIO filter setting
|
||||
filterArray[TS_AUDIO0].type.mainType = DemuxFilterMainType::TS;
|
||||
filterArray[TS_AUDIO0].type.subType.tsFilterType(DemuxTsFilterType::AUDIO);
|
||||
filterArray[TS_AUDIO0].bufferSize = FMQ_SIZE_16M;
|
||||
filterArray[TS_AUDIO0].settings.ts().tpid = 256;
|
||||
filterArray[TS_AUDIO0].settings.ts().filterSettings.av({.isPassthrough = false});
|
||||
filterArray[TS_AUDIO1].type.mainType = DemuxFilterMainType::TS;
|
||||
filterArray[TS_AUDIO1].type.subType.tsFilterType(DemuxTsFilterType::AUDIO);
|
||||
filterArray[TS_AUDIO1].bufferSize = FMQ_SIZE_16M;
|
||||
filterArray[TS_AUDIO1].settings.ts().tpid = 257;
|
||||
filterArray[TS_AUDIO1].settings.ts().filterSettings.av({.isPassthrough = false});
|
||||
// TS PES filter setting
|
||||
filterArray[TS_PES0].type.mainType = DemuxFilterMainType::TS;
|
||||
filterArray[TS_PES0].type.subType.tsFilterType(DemuxTsFilterType::PES);
|
||||
filterArray[TS_PES0].bufferSize = FMQ_SIZE_16M;
|
||||
filterArray[TS_PES0].settings.ts().tpid = 256;
|
||||
filterArray[TS_PES0].settings.ts().filterSettings.pesData({
|
||||
.isRaw = false,
|
||||
.streamId = 0xbd,
|
||||
});
|
||||
// TS PCR filter setting
|
||||
filterArray[TS_PCR0].type.mainType = DemuxFilterMainType::TS;
|
||||
filterArray[TS_PCR0].type.subType.tsFilterType(DemuxTsFilterType::PCR);
|
||||
filterArray[TS_PCR0].bufferSize = FMQ_SIZE_16M;
|
||||
filterArray[TS_PCR0].settings.ts().tpid = 256;
|
||||
filterArray[TS_PCR0].settings.ts().filterSettings.noinit();
|
||||
// TS filter setting
|
||||
filterArray[TS_TS0].type.mainType = DemuxFilterMainType::TS;
|
||||
filterArray[TS_TS0].type.subType.tsFilterType(DemuxTsFilterType::TS);
|
||||
filterArray[TS_TS0].bufferSize = FMQ_SIZE_16M;
|
||||
filterArray[TS_TS0].settings.ts().tpid = 256;
|
||||
filterArray[TS_TS0].settings.ts().filterSettings.noinit();
|
||||
// TS SECTION filter setting
|
||||
filterArray[TS_SECTION0].type.mainType = DemuxFilterMainType::TS;
|
||||
filterArray[TS_SECTION0].type.subType.tsFilterType(DemuxTsFilterType::SECTION);
|
||||
filterArray[TS_SECTION0].bufferSize = FMQ_SIZE_16M;
|
||||
filterArray[TS_SECTION0].settings.ts().tpid = 256;
|
||||
filterArray[TS_SECTION0].settings.ts().filterSettings.section({
|
||||
.isRaw = false,
|
||||
});
|
||||
// TS RECORD filter setting
|
||||
filterArray[TS_RECORD0].type.mainType = DemuxFilterMainType::TS;
|
||||
filterArray[TS_RECORD0].type.subType.tsFilterType(DemuxTsFilterType::RECORD);
|
||||
filterArray[TS_RECORD0].settings.ts().tpid = 81;
|
||||
filterArray[TS_RECORD0].settings.ts().filterSettings.record({
|
||||
.scIndexType = DemuxRecordScIndexType::NONE,
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user