mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 22:04:26 +00:00
Merge changes from topic "filters"
* changes: VTS refactoring for filter separation Refactor Tuner HAL Default Impl for Filter and Dvr separation Add Filter linkage and seperate interface
This commit is contained in:
@@ -9,15 +9,21 @@ hidl_interface {
|
||||
srcs: [
|
||||
"types.hal",
|
||||
"IDemux.hal",
|
||||
"IDemuxCallback.hal",
|
||||
"IDescrambler.hal",
|
||||
"IDvr.hal",
|
||||
"IDvrCallback.hal",
|
||||
"IFilter.hal",
|
||||
"IFilterCallback.hal",
|
||||
"IFrontend.hal",
|
||||
"IFrontendCallback.hal",
|
||||
"ILnb.hal",
|
||||
"ILnbCallback.hal",
|
||||
"ITimeFilter.hal",
|
||||
"ITuner.hal",
|
||||
],
|
||||
interfaces: [
|
||||
"android.hidl.base@1.0",
|
||||
"android.hidl.safe_union@1.0",
|
||||
],
|
||||
gen_java: false,
|
||||
gen_java_constants: true,
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
package android.hardware.tv.tuner@1.0;
|
||||
|
||||
import IDemuxCallback;
|
||||
import IDvr;
|
||||
import IDvrCallback;
|
||||
import IFilter;
|
||||
import IFilterCallback;
|
||||
import ITimeFilter;
|
||||
|
||||
/**
|
||||
* Demultiplexer(Demux) takes a single multiplexed input and splits it into
|
||||
@@ -24,7 +28,6 @@ import IDemuxCallback;
|
||||
*
|
||||
*/
|
||||
interface IDemux {
|
||||
|
||||
/**
|
||||
* Set a frontend resource as data input of the demux
|
||||
*
|
||||
@@ -39,134 +42,51 @@ interface IDemux {
|
||||
setFrontendDataSource(FrontendId frontendId) generates (Result result);
|
||||
|
||||
/**
|
||||
* Add a filter to the demux
|
||||
* Open a new filter in the demux
|
||||
*
|
||||
* It is used by the client to add a filter to the demux.
|
||||
* It is used by the client to open a filter in the demux.
|
||||
*
|
||||
* @param type the type of the filter to be added.
|
||||
* @param bufferSize the buffer size of the filter to be added. It's used to
|
||||
* create a FMQ(Fast Message Queue) to hold data output from the filter.
|
||||
* @param bufferSize the buffer size of the filter to be opened. It's used
|
||||
* to create a FMQ(Fast Message Queue) to hold data output from the filter.
|
||||
* @param cb the callback for the filter to be used to send notifications
|
||||
* back to the client.
|
||||
* @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 ID of the newly added filter.
|
||||
* @return filter the filter instance of the newly added.
|
||||
*/
|
||||
addFilter(DemuxFilterType type, uint32_t bufferSize, IDemuxCallback cb)
|
||||
generates (Result result, DemuxFilterId filterId);
|
||||
openFilter(DemuxFilterType type, uint32_t bufferSize, IFilterCallback cb)
|
||||
generates (Result result, IFilter filter);
|
||||
|
||||
/**
|
||||
* Get the descriptor of the filter's FMQ
|
||||
* Open time filter of the demux
|
||||
*
|
||||
* It is used by the client to get the descriptor of the filter's Fast
|
||||
* Message Queue. The data in FMQ is filtered out from MPEG transport
|
||||
* stream. The data is origanized to data blocks which may have
|
||||
* different length. The length's information of one or multiple data blocks
|
||||
* is sent to client throught DemuxFilterEvent.
|
||||
* It is used by the client to open time filter of the demux.
|
||||
*
|
||||
* @param filterId the ID of the filter.
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_ARGUMENT if failed for wrong filter ID.
|
||||
* UNAVAILABLE if time filter is not supported.
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
* @return queue the descriptor of the filter's FMQ
|
||||
* @return timeFilter the time filter instance of the newly added.
|
||||
*/
|
||||
getFilterQueueDesc(DemuxFilterId filterId)
|
||||
generates (Result result, fmq_sync<uint8_t> queue);
|
||||
|
||||
/**
|
||||
* Configure the filter.
|
||||
*
|
||||
* It is used by the client to configure the filter so that it can filter out
|
||||
* intended data.
|
||||
*
|
||||
* @param filterId the ID of the filter.
|
||||
* @param settings the settings of the filter.
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_ARGUMENT if failed for wrong filter ID.
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
configureFilter(DemuxFilterId filterId, DemuxFilterSettings settings)
|
||||
generates(Result result);
|
||||
|
||||
/**
|
||||
* Start the filter.
|
||||
*
|
||||
* It is used by the client to ask the filter to start filterring data.
|
||||
*
|
||||
* @param filterId the ID of the filter.
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_ARGUMENT if failed for wrong filter ID.
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
startFilter(DemuxFilterId filterId) generates (Result result);
|
||||
|
||||
/**
|
||||
* Stop the filter.
|
||||
*
|
||||
* It is used by the client to ask the filter to stop filterring data.
|
||||
* It won't discard the data already filtered out by the filter. The filter
|
||||
* will be stopped and removed automatically if the demux is closed.
|
||||
*
|
||||
* @param filterId the ID of the filter.
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_ARGUMENT if failed for wrong filter ID.
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
stopFilter(DemuxFilterId filterId) generates (Result result);
|
||||
|
||||
/**
|
||||
* Flush the filter.
|
||||
*
|
||||
* It is used by the client to ask the filter to flush the data which is
|
||||
* already produced but not consumed yet.
|
||||
*
|
||||
* @param filterId the ID of the filter.
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_ARGUMENT if failed for wrong filter ID.
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
flushFilter(DemuxFilterId filterId) generates (Result result);
|
||||
|
||||
/**
|
||||
* Remove a filter from the demux
|
||||
*
|
||||
* It is used by the client to remove a filter from the demux.
|
||||
*
|
||||
* @param filterId the ID of the removed filter.
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_ARGUMENT if failed for wrong filter ID.
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
removeFilter(DemuxFilterId filterId) generates (Result result);
|
||||
openTimeFilter() generates (Result result, ITimeFilter timeFilter);
|
||||
|
||||
/**
|
||||
* Get hardware sync ID for audio and video.
|
||||
*
|
||||
* It is used by the client to get the hardware sync ID for audio and video.
|
||||
*
|
||||
* @param filterId the ID of the filter.
|
||||
* @param filter the filter instance.
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_ARGUMENT if failed for a wrong filter ID.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
* @return avSyncHwId the id of hardware A/V sync.
|
||||
*/
|
||||
getAvSyncHwId(DemuxFilterId filterId)
|
||||
generates (Result result, AvSyncHwId avSyncHwId);
|
||||
getAvSyncHwId(IFilter filter) generates (Result result, AvSyncHwId avSyncHwId);
|
||||
|
||||
/**
|
||||
* Get current time stamp to use for A/V sync
|
||||
@@ -182,8 +102,7 @@ interface IDemux {
|
||||
* @return time the current time stamp of hardware A/V sync. The time stamp
|
||||
* based on 90KHz has the same format as PTS (Presentation Time Stamp).
|
||||
*/
|
||||
getAvSyncTime(AvSyncHwId avSyncHwId)
|
||||
generates (Result result, uint64_t time);
|
||||
getAvSyncTime(AvSyncHwId avSyncHwId) generates (Result result, uint64_t time);
|
||||
|
||||
/**
|
||||
* Close the Demux instance
|
||||
@@ -198,218 +117,21 @@ interface IDemux {
|
||||
close() generates (Result result);
|
||||
|
||||
/**
|
||||
* Add output to the demux
|
||||
* Open a DVR (Digital Video Record) instance in the demux
|
||||
*
|
||||
* It is used by the client to record output data from selected filters.
|
||||
* It is used by the client to record and playback.
|
||||
*
|
||||
* @param type specify which kind of DVR to open.
|
||||
* @param bufferSize the buffer size of the output to be added. It's used to
|
||||
* create a FMQ(Fast Message Queue) to hold data from selected filters.
|
||||
* @param cb the callback for the demux to be used to send notifications
|
||||
* @param cb the callback for the DVR to be used to send notifications
|
||||
* back to the client.
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* OUT_OF_MEMORY if failed for not enough memory.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
* @return dvr a DVR instance.
|
||||
*/
|
||||
addOutput(uint32_t bufferSize, IDemuxCallback cb) generates (Result result);
|
||||
|
||||
/**
|
||||
* Get the descriptor of the output's FMQ
|
||||
*
|
||||
* It is used by the client to get the descriptor of the output's Fast
|
||||
* Message Queue. The data in FMQ is muxed packets output from selected
|
||||
* filters. The packet's format is specifed by DemuxDataFormat in
|
||||
* DemuxOutputSettings.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
* @return queue the descriptor of the output's FMQ
|
||||
*/
|
||||
getOutputQueueDesc() generates (Result result, fmq_sync<uint8_t> queue);
|
||||
|
||||
/**
|
||||
* Configure the demux's output.
|
||||
*
|
||||
* It is used by the client to configure the demux's output for recording.
|
||||
*
|
||||
* @param settings the settings of the demux's output.
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
configureOutput(DemuxOutputSettings settings) generates (Result result);
|
||||
|
||||
/**
|
||||
* Attach one filter to the demux's output.
|
||||
*
|
||||
* It is used by the client to mux one filter's output to demux's output.
|
||||
*
|
||||
* @param filterId the ID of the attached 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.
|
||||
*/
|
||||
attachOutputFilter(DemuxFilterId filterId) generates (Result result);
|
||||
|
||||
/**
|
||||
* Detach one filter from the demux's output.
|
||||
*
|
||||
* It is used by the client to remove one filter's output from demux's
|
||||
* output.
|
||||
*
|
||||
* @param filterId the ID of the detached 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.
|
||||
*/
|
||||
detachOutputFilter(DemuxFilterId filterId) generates (Result result);
|
||||
|
||||
/**
|
||||
* Start to take data to the demux's output.
|
||||
*
|
||||
* It is used by the client to ask the output to start to take data from
|
||||
* attached filters.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
startOutput() generates (Result result);
|
||||
|
||||
/**
|
||||
* Stop to take data to the demux's output.
|
||||
*
|
||||
* It is used by the client to ask the output to stop to take data from
|
||||
* attached filters.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
stopOutput() generates (Result result);
|
||||
|
||||
/**
|
||||
* Flush unconsumed data in the demux's output.
|
||||
*
|
||||
* It is used by the client to ask the demux to flush the data which is
|
||||
* already produced but not consumed yet in the demux's output.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
flushOutput() generates (Result result);
|
||||
|
||||
/**
|
||||
* Remove the demux's output.
|
||||
*
|
||||
* It is used by the client to remove the demux's output.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
removeOutput() generates (Result result);
|
||||
|
||||
/**
|
||||
* Add input to the demux
|
||||
*
|
||||
* It is used by the client to add the demux's input for playback content.
|
||||
*
|
||||
* @param bufferSize the buffer size of the demux's input to be added.
|
||||
* It's used to create a FMQ(Fast Message Queue) to hold input data.
|
||||
* @param cb the callback for the demux to be used to send notifications
|
||||
* back to the client.
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* OUT_OF_MEMORY if failed for not enough memory.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
addInput(uint32_t bufferSize, IDemuxCallback cb) generates (Result result);
|
||||
|
||||
/**
|
||||
* Get the descriptor of the input's FMQ
|
||||
*
|
||||
* It is used by the client to get the descriptor of the input's Fast
|
||||
* Message Queue. The data in FMQ is fed by client. Data format is specifed
|
||||
* by DemuxDataFormat in DemuxInputSettings.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
* @return queue the descriptor of the output's FMQ
|
||||
*/
|
||||
getInputQueueDesc() generates (Result result, fmq_sync<uint8_t> queue);
|
||||
|
||||
/**
|
||||
* Configure the demux's input.
|
||||
*
|
||||
* It is used by the client to configure the demux's input for playback.
|
||||
*
|
||||
* @param settings the settings of the demux's input.
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
configureInput(DemuxInputSettings settings) generates (Result result);
|
||||
|
||||
/**
|
||||
* Start to consume the data from the demux's input.
|
||||
*
|
||||
* It is used by the client to ask the demux to start to consume data from
|
||||
* the demux's input.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
startInput() generates (Result result);
|
||||
|
||||
/**
|
||||
* Stop to consume the data from the demux's input.
|
||||
*
|
||||
* It is used by the client to ask the demux to stop to consume data from
|
||||
* the demux's input.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
stopInput() generates (Result result);
|
||||
|
||||
/**
|
||||
* Flush unconsumed data in the demux's input.
|
||||
*
|
||||
* It is used by the client to ask the demux to flush the data which is
|
||||
* already produced but not consumed yet in the demux's input.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
flushInput() generates (Result result);
|
||||
|
||||
/**
|
||||
* Remove the demux's input.
|
||||
*
|
||||
* It is used by the client to remove the demux's input.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
removeInput() generates (Result result);
|
||||
openDvr(DvrType type, uint32_t bufferSize, IDvrCallback cb)
|
||||
generates (Result result, IDvr dvr);
|
||||
};
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
|
||||
package android.hardware.tv.tuner@1.0;
|
||||
|
||||
import IFilter;
|
||||
|
||||
/**
|
||||
* Descrambler is used to descramble input data.
|
||||
*
|
||||
@@ -59,12 +62,13 @@ interface IDescrambler {
|
||||
* packets from different PIDs.
|
||||
*
|
||||
* @param pid the PID of packets to start to be descrambled.
|
||||
* @param filter an optional filter instance to identify upper stream.
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
addPid(DemuxTpid pid) generates (Result result);
|
||||
addPid(DemuxPid pid, IFilter optionalSourceFilter) generates (Result result);
|
||||
|
||||
/**
|
||||
* Remove packets' PID from the descrambler
|
||||
@@ -73,12 +77,13 @@ interface IDescrambler {
|
||||
* descrambler stop to descramble.
|
||||
*
|
||||
* @param pid the PID of packets to stop to be descrambled.
|
||||
* @param filter an optional filter instance to identify upper stream.
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
removePid(DemuxTpid pid) generates (Result result);
|
||||
removePid(DemuxPid pid, IFilter optionalSourceFilter) generates (Result result);
|
||||
|
||||
/**
|
||||
* Release the descrambler instance
|
||||
@@ -92,4 +97,3 @@ interface IDescrambler {
|
||||
*/
|
||||
close() generates (Result result);
|
||||
};
|
||||
|
||||
|
||||
133
tv/tuner/1.0/IDvr.hal
Normal file
133
tv/tuner/1.0/IDvr.hal
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.tv.tuner@1.0;
|
||||
|
||||
import IFilter;
|
||||
|
||||
/**
|
||||
* Digtal Video Record (DVR) interface provides record control on Demux's
|
||||
* output buffer and playback control on Demux's input buffer.
|
||||
*/
|
||||
interface IDvr {
|
||||
/**
|
||||
* Get the descriptor of the DVR's FMQ
|
||||
*
|
||||
* It is used by the client to get the descriptor of the DVR's Fast
|
||||
* Message Queue. The FMQ is used to transfer record or playback data
|
||||
* between the client and the HAL.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
* @return queue the descriptor of the DVR's FMQ
|
||||
*/
|
||||
getQueueDesc() generates (Result result, fmq_sync<uint8_t> queue);
|
||||
|
||||
/**
|
||||
* Configure the DVR.
|
||||
*
|
||||
* It is used by the client to configure the DVR interface.
|
||||
*
|
||||
* @param settings the settings of the DVR interface.
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
configure(DvrSettings settings) generates (Result result);
|
||||
|
||||
/**
|
||||
* Attach one filter to DVR interface for recording.
|
||||
*
|
||||
* It is used by the client to add the data filtered out from the filter
|
||||
* to record.
|
||||
*
|
||||
* @param filter the instance of the attached 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.
|
||||
*/
|
||||
attachFilter(IFilter filter) generates (Result result);
|
||||
|
||||
/**
|
||||
* Detach one filter from the DVR's recording.
|
||||
*
|
||||
* It is used by the client to remove the data of the filter from DVR's
|
||||
* recording.
|
||||
*
|
||||
* @param filter the instance of the detached 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.
|
||||
*/
|
||||
detachFilter(IFilter filter) generates (Result result);
|
||||
|
||||
/**
|
||||
* Start DVR.
|
||||
*
|
||||
* It is used by the client to ask the DVR to start consuming playback data
|
||||
* or producing data for record.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
start() generates (Result result);
|
||||
|
||||
/**
|
||||
* Stop DVR.
|
||||
*
|
||||
* It is used by the client to ask the DVR to stop consuming playback data
|
||||
* or producing data for record.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
stop() generates (Result result);
|
||||
|
||||
/**
|
||||
* Flush DVR data.
|
||||
*
|
||||
* It is used by the client to ask the DVR to flush the data which is
|
||||
* not consumed by HAL for playback or the client for record yet.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
flush() generates (Result result);
|
||||
|
||||
/**
|
||||
* close the DVR instance to release resource for DVR.
|
||||
*
|
||||
* It is used by the client to close the DVR instance, and HAL clears
|
||||
* underneath resource for this DVR instance. Client mustn't access the
|
||||
* instance any more and all methods should return a failure.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
close() generates (Result result);
|
||||
};
|
||||
33
tv/tuner/1.0/IDvrCallback.hal
Normal file
33
tv/tuner/1.0/IDvrCallback.hal
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.tv.tuner@1.0;
|
||||
|
||||
interface IDvrCallback {
|
||||
/**
|
||||
* Notify the client a new status of the demux's record.
|
||||
*
|
||||
* @param status a new status of the demux's record.
|
||||
*/
|
||||
oneway onRecordStatus(RecordStatus status);
|
||||
|
||||
/**
|
||||
* Notify the client a new status of the demux's playback.
|
||||
*
|
||||
* @param status a new status of the demux's playback.
|
||||
*/
|
||||
oneway onPlaybackStatus(PlaybackStatus status);
|
||||
};
|
||||
143
tv/tuner/1.0/IFilter.hal
Normal file
143
tv/tuner/1.0/IFilter.hal
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.tv.tuner@1.0;
|
||||
|
||||
import IFilterCallback;
|
||||
|
||||
/**
|
||||
* The Filter is used to filter wanted data according to the filter's
|
||||
* configuration.
|
||||
*/
|
||||
interface IFilter {
|
||||
/**
|
||||
* Get the descriptor of the filter's FMQ
|
||||
*
|
||||
* It is used by the client to get the descriptor of the filter's Fast
|
||||
* Message Queue. The data in FMQ is filtered out from demux input or upper
|
||||
* stream's filter. The data is origanized to data blocks which may have
|
||||
* different length. The length's information of one or multiple data blocks
|
||||
* is sent to client through DemuxFilterEvent. The data in each block
|
||||
* follows the stardard specified by filter's type.
|
||||
* E.X. one data block from the filter with Main_Type==TS and Sub_Type==PES
|
||||
* is Packetized Elementary Stream from Transport Stream according to
|
||||
* ISO/IEC 13818-1.
|
||||
*
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* UNAVAILABLE if the filter doesn't have FMQ.
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
* @return queue the descriptor of the filter's FMQ
|
||||
*/
|
||||
getQueueDesc() generates (Result result, fmq_sync<uint8_t> queue);
|
||||
|
||||
/**
|
||||
* Configure the filter.
|
||||
*
|
||||
* It is used by the client to configure the filter so that it can filter out
|
||||
* intended data.
|
||||
*
|
||||
* @param settings the settings of 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.
|
||||
*/
|
||||
configure(DemuxFilterSettings settings) generates (Result result);
|
||||
|
||||
/**
|
||||
* Start the filter.
|
||||
*
|
||||
* It is used by the client to ask the filter to start filterring data.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
start() generates (Result result);
|
||||
|
||||
/**
|
||||
* Stop the filter.
|
||||
*
|
||||
* It is used by the client to ask the filter to stop filterring data.
|
||||
* It won't discard the data already filtered out by the filter. The filter
|
||||
* will be stopped and removed automatically if the demux is closed.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
stop() generates (Result result);
|
||||
|
||||
/**
|
||||
* Flush the filter.
|
||||
*
|
||||
* It is used by the client to ask the filter to flush the data which is
|
||||
* already produced but not consumed yet.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
flush() generates (Result result);
|
||||
|
||||
/**
|
||||
* Get the filter Id.
|
||||
*
|
||||
* It is used by the client to ask the hardware resource id for the filter.
|
||||
*
|
||||
* @param filterId 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.
|
||||
*/
|
||||
getId() generates (Result result, uint32_t filterId);
|
||||
|
||||
/**
|
||||
* Set the filter's data source.
|
||||
*
|
||||
* A filter uses demux as data source by default. If the data was packetized
|
||||
* by multiple protocols, multiple filters may need to work together to
|
||||
* extract all protocols' header. Then a filter's data source can be output
|
||||
* from another filter.
|
||||
*
|
||||
* @param filter the filter instance which provides data input. Switch to
|
||||
* use demux as data source if the filter instance is NULL.
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if failed for wrong state.
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
setDataSource(IFilter filter) generates (Result result);
|
||||
|
||||
/**
|
||||
* Release the Filter instance
|
||||
*
|
||||
* It is used by the client to release the Filter instance. HAL clear
|
||||
* underneath resource. client mustn't access the instance any more.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
close() generates (Result result);
|
||||
};
|
||||
@@ -16,34 +16,18 @@
|
||||
|
||||
package android.hardware.tv.tuner@1.0;
|
||||
|
||||
interface IDemuxCallback {
|
||||
interface IFilterCallback {
|
||||
/**
|
||||
* Notify the client that a new filter event happened.
|
||||
*
|
||||
* @param filterEvent a demux filter event.
|
||||
* @param filterEvent a filter event.
|
||||
*/
|
||||
oneway onFilterEvent(DemuxFilterEvent filterEvent);
|
||||
|
||||
/**
|
||||
* Notify the client a new status of a demux filter.
|
||||
* Notify the client a new status of a filter.
|
||||
*
|
||||
* @param filterId the demux filter ID.
|
||||
* @param status a new status of the demux filter.
|
||||
* @param status a new status of the filter.
|
||||
*/
|
||||
oneway onFilterStatus(DemuxFilterId filterId, DemuxFilterStatus status);
|
||||
|
||||
/**
|
||||
* Notify the client a new status of the demux's output.
|
||||
*
|
||||
* @param status a new status of the demux's output.
|
||||
*/
|
||||
oneway onOutputStatus(DemuxOutputStatus status);
|
||||
|
||||
/**
|
||||
* Notify the client a new status of the demux's input.
|
||||
*
|
||||
* @param status a new status of the demux's input.
|
||||
*/
|
||||
oneway onInputStatus(DemuxInputStatus status);
|
||||
oneway onFilterStatus(DemuxFilterStatus status);
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.tv.tuner@1.0;
|
||||
|
||||
import IFrontendCallback;
|
||||
@@ -126,7 +127,8 @@ interface IFrontend {
|
||||
* @return statuses an array of statuses which response the caller's
|
||||
* request.
|
||||
*/
|
||||
getStatus(vec<FrontendStatusType> statusTypes) generates (Result result, vec<FrontendStatus> statuses);
|
||||
getStatus(vec<FrontendStatusType> statusTypes)
|
||||
generates (Result result, vec<FrontendStatus> statuses);
|
||||
|
||||
/**
|
||||
* Sets Low-Noise Block downconverter (LNB) for satellite frontend.
|
||||
|
||||
@@ -24,16 +24,6 @@ interface IFrontendCallback {
|
||||
*/
|
||||
oneway onEvent(FrontendEventType frontendEventType);
|
||||
|
||||
/**
|
||||
* The callback function that must be called by HAL implementation to notify
|
||||
* the client of new DiSEqC message.
|
||||
*
|
||||
* @param diseqcMessage a byte array of data for DiSEqC (Digital Satellite
|
||||
* Equipment Control) message which is specified by EUTELSAT Bus Functional
|
||||
* Specification Version 4.2.
|
||||
*/
|
||||
oneway onDiseqcMessage(vec<uint8_t> diseqcMessage);
|
||||
|
||||
/**
|
||||
* The callback function that must be called by HAL implementation to notify
|
||||
* the client of scan messages.
|
||||
|
||||
@@ -13,14 +13,34 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.tv.tuner@1.0;
|
||||
|
||||
import ILnbCallback;
|
||||
|
||||
/**
|
||||
* A Tuner LNB (low-noise block downconverter) is used by satellite frontend
|
||||
* to receive the microwave signal from the satellite, amplify it, and
|
||||
* downconvert the frequency to a lower frequency.
|
||||
*/
|
||||
interface ILnb {
|
||||
/**
|
||||
* Set the lnb callback.
|
||||
*
|
||||
* ILnbCallback is used by the client to receive events from the Lnb.
|
||||
* Only one callback per ILnb instance is supported. The callback
|
||||
* will be replaced if it's set again.
|
||||
*
|
||||
* @param callback Callback object to pass Lnb events to the system.
|
||||
* The previously registered callback must be replaced with this one.
|
||||
* It can be null.
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* INVALID_STATE if callback can't be set at current stage,
|
||||
* UNKNOWN_ERROR if callback setting failed for other reasons.
|
||||
*/
|
||||
setCallback(ILnbCallback callback) generates (Result result);
|
||||
|
||||
/**
|
||||
* Set the lnb's power voltage.
|
||||
*
|
||||
@@ -30,7 +50,7 @@ interface ILnb {
|
||||
* INVALID_ARGUMENT if the selected voltage isn't allowed,
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
setVoltage(FrontendLnbVoltage voltage) generates (Result result);
|
||||
setVoltage(LnbVoltage voltage) generates (Result result);
|
||||
|
||||
/**
|
||||
* Set the lnb's tone mode.
|
||||
@@ -41,7 +61,7 @@ interface ILnb {
|
||||
* INVALID_ARGUMENT if the selected tone mode isn't allowed,
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
setTone(FrontendLnbTone tone) generates (Result result);
|
||||
setTone(LnbTone tone) generates (Result result);
|
||||
|
||||
/**
|
||||
* Select the lnb's position.
|
||||
@@ -52,7 +72,7 @@ interface ILnb {
|
||||
* INVALID_ARGUMENT if the selected position isn't allowed,
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
setSatellitePosition(FrontendLnbPosition position) generates (Result result);
|
||||
setSatellitePosition(LnbPosition position) generates (Result result);
|
||||
|
||||
/**
|
||||
* Sends DiSEqC (Digital Satellite Equipment Control) message.
|
||||
|
||||
36
tv/tuner/1.0/ILnbCallback.hal
Normal file
36
tv/tuner/1.0/ILnbCallback.hal
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.tv.tuner@1.0;
|
||||
|
||||
interface ILnbCallback {
|
||||
/**
|
||||
* Notify the client that a new event happened on the Lnb.
|
||||
*
|
||||
* @param LnbEventType the event type.
|
||||
*/
|
||||
oneway onEvent(LnbEventType lnbEventType);
|
||||
|
||||
/**
|
||||
* The callback function that must be called by HAL implementation to notify
|
||||
* the client of new DiSEqC message.
|
||||
*
|
||||
* @param diseqcMessage a byte array of data for DiSEqC (Digital Satellite
|
||||
* Equipment Control) message which is specified by EUTELSAT Bus Functional
|
||||
* Specification Version 4.2.
|
||||
*/
|
||||
oneway onDiseqcMessage(vec<uint8_t> diseqcMessage);
|
||||
};
|
||||
88
tv/tuner/1.0/ITimeFilter.hal
Normal file
88
tv/tuner/1.0/ITimeFilter.hal
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.tv.tuner@1.0;
|
||||
|
||||
/**
|
||||
* Timer Filter is used by Demux to filter data based on time stamp.
|
||||
*/
|
||||
interface ITimeFilter {
|
||||
/**
|
||||
* Set time stamp for time based filter.
|
||||
*
|
||||
* It is used by the client to set initial time stamp and enable time
|
||||
* filtering. The time will be incremented locally. The demux discards
|
||||
* the content which time stamp is older than the time in the time filter.
|
||||
*
|
||||
* @param timeStamp initial time stamp for the time filter. It based on
|
||||
* 90KHz has the same format as PTS (Presentation Time Stamp).
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
setTimeStamp(uint64_t timeStamp) generates (Result result);
|
||||
|
||||
/**
|
||||
* Clear the time stamp in the time filter.
|
||||
*
|
||||
* It is used by the client to clear the time value of the time filter,
|
||||
* then disable time filter.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
clearTimeStamp() generates (Result result);
|
||||
|
||||
/**
|
||||
* Get the current time in the time filter.
|
||||
*
|
||||
* It is used by the client to inquiry current time in the time 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 timeStamp current time stamp in the time filter.
|
||||
*/
|
||||
getTimeStamp() generates (Result result, uint64_t timeStamp);
|
||||
|
||||
/**
|
||||
* Get the time from the beginning of current data source.
|
||||
*
|
||||
* It is used by the client to inquiry the time stamp from the beginning
|
||||
* of current data source.
|
||||
*
|
||||
* @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 timeStamp time stamp from the beginning of current data source.
|
||||
*/
|
||||
getSourceTime() generates (Result result, uint64_t timeStamp);
|
||||
|
||||
/**
|
||||
* Close the Time Filter instance
|
||||
*
|
||||
* It is used by the client to release the demux instance. HAL clear
|
||||
* underneath resource. client mustn't access the instance any more.
|
||||
*
|
||||
* @return result Result status of the operation.
|
||||
* SUCCESS if successful,
|
||||
* UNKNOWN_ERROR if failed for other reasons.
|
||||
*/
|
||||
close() generates (Result result);
|
||||
};
|
||||
@@ -50,8 +50,7 @@ interface ITuner {
|
||||
* UNKNOWN_ERROR if creation failed for other reasons.
|
||||
* @return frontend the newly created frontend interface.
|
||||
*/
|
||||
openFrontendById(FrontendId frontendId)
|
||||
generates (Result result, IFrontend frontend);
|
||||
openFrontendById(FrontendId frontendId) generates (Result result, IFrontend frontend);
|
||||
|
||||
/**
|
||||
* Create a new instance of Demux.
|
||||
@@ -64,8 +63,7 @@ interface ITuner {
|
||||
* @return demuxId newly created demux id.
|
||||
* @return demux the newly created demux interface.
|
||||
*/
|
||||
openDemux()
|
||||
generates (Result result, DemuxId demuxId, IDemux demux);
|
||||
openDemux() generates (Result result, DemuxId demuxId, IDemux demux);
|
||||
|
||||
/**
|
||||
* Retrieve the Demux's Capabilities.
|
||||
@@ -87,8 +85,7 @@ interface ITuner {
|
||||
* UNKNOWN_ERROR if creation failed for other reasons.
|
||||
* @return descrambler the newly created descrambler interface.
|
||||
*/
|
||||
openDescrambler()
|
||||
generates (Result result, IDescrambler descrambler);
|
||||
openDescrambler() generates (Result result, IDescrambler descrambler);
|
||||
|
||||
/**
|
||||
* Retrieve the frontend's information.
|
||||
@@ -99,8 +96,7 @@ interface ITuner {
|
||||
* UNKNOWN_ERROR if the inquiry failed for other reasons.
|
||||
* @return info the frontend's information.
|
||||
*/
|
||||
getFrontendInfo(FrontendId frontendId)
|
||||
generates (Result result, FrontendInfo info);
|
||||
getFrontendInfo(FrontendId frontendId) generates (Result result, FrontendInfo info);
|
||||
|
||||
/**
|
||||
* Get low-noise block downconverter (LNB) IDs.
|
||||
@@ -126,7 +122,5 @@ interface ITuner {
|
||||
* UNKNOWN_ERROR if creation failed for other reasons.
|
||||
* @return lnb the newly created Lnb interface.
|
||||
*/
|
||||
openLnbById(LnbId lnbId)
|
||||
generates (Result result, ILnb lnb);
|
||||
openLnbById(LnbId lnbId) generates (Result result, ILnb lnb);
|
||||
};
|
||||
|
||||
|
||||
@@ -4,9 +4,12 @@ cc_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",
|
||||
|
||||
@@ -28,45 +28,6 @@ namespace implementation {
|
||||
|
||||
#define WAIT_TIMEOUT 3000000000
|
||||
|
||||
const std::vector<uint8_t> fakeDataInputBuffer{
|
||||
0x00, 0x00, 0x00, 0x01, 0x09, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x1e, 0xdb,
|
||||
0x01, 0x40, 0x16, 0xec, 0x04, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x0f, 0x03,
|
||||
0xc5, 0x8b, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x68, 0xca, 0x8c, 0xb2, 0x00, 0x00, 0x01, 0x06,
|
||||
0x05, 0xff, 0xff, 0x70, 0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8,
|
||||
0x20, 0xd9, 0x23, 0xee, 0xef, 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63, 0x6f, 0x72,
|
||||
0x65, 0x20, 0x31, 0x34, 0x32, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32, 0x36, 0x34, 0x2f, 0x4d,
|
||||
0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, 0x43, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x63,
|
||||
0x20, 0x2d, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30,
|
||||
0x33, 0x2d, 0x32, 0x30, 0x31, 0x34, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
|
||||
0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c, 0x61, 0x6e, 0x2e, 0x6f,
|
||||
0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20, 0x2d, 0x20,
|
||||
0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x20, 0x63, 0x61, 0x62, 0x61, 0x63, 0x3d,
|
||||
0x30, 0x20, 0x72, 0x65, 0x66, 0x3d, 0x32, 0x20, 0x64, 0x65, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
|
||||
0x3d, 0x31, 0x3a, 0x30, 0x3a, 0x30, 0x20, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x65, 0x3d,
|
||||
0x30, 0x78, 0x31, 0x3a, 0x30, 0x78, 0x31, 0x31, 0x31, 0x20, 0x6d, 0x65, 0x3d, 0x68, 0x65,
|
||||
0x78, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x65, 0x3d, 0x37, 0x20, 0x70, 0x73, 0x79, 0x3d, 0x31,
|
||||
0x20, 0x70, 0x73, 0x79, 0x5f, 0x72, 0x64, 0x3d, 0x31, 0x2e, 0x30, 0x30, 0x3a, 0x30, 0x2e,
|
||||
0x30, 0x30, 0x20, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x3d, 0x31, 0x20,
|
||||
0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x3d, 0x31, 0x36, 0x20, 0x63, 0x68, 0x72,
|
||||
0x6f, 0x6d, 0x61, 0x5f, 0x6d, 0x65, 0x3d, 0x31, 0x20, 0x74, 0x72, 0x65, 0x6c, 0x6c, 0x69,
|
||||
0x73, 0x3d, 0x31, 0x20, 0x38, 0x78, 0x38, 0x64, 0x63, 0x74, 0x3d, 0x30, 0x20, 0x63, 0x71,
|
||||
0x6d, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x61, 0x64, 0x7a, 0x6f, 0x6e, 0x65, 0x3d, 0x32, 0x31,
|
||||
0x2c, 0x31, 0x31, 0x20, 0x66, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x73, 0x6b, 0x69, 0x70, 0x3d,
|
||||
0x31, 0x20, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x5f, 0x71, 0x70, 0x5f, 0x6f, 0x66, 0x66,
|
||||
0x73, 0x65, 0x74, 0x3d, 0x2d, 0x32, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d,
|
||||
0x36, 0x30, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x61, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x68,
|
||||
0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x35, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x64, 0x5f,
|
||||
0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x30, 0x20, 0x6e, 0x72, 0x3d, 0x30, 0x20,
|
||||
0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x20, 0x69, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x3d, 0x30, 0x20, 0x62, 0x6c, 0x75, 0x72, 0x61, 0x79,
|
||||
0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x3d, 0x30, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74,
|
||||
0x72, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x3d, 0x30, 0x20,
|
||||
0x62, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x3d, 0x30, 0x20, 0x77, 0x65, 0x69, 0x67, 0x68,
|
||||
0x74, 0x70, 0x3d, 0x30, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x3d, 0x32, 0x35, 0x30,
|
||||
0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x3d, 0x32, 0x35, 0x20,
|
||||
0x73, 0x63, 0x65, 0x6e, 0x65,
|
||||
};
|
||||
|
||||
Demux::Demux(uint32_t demuxId, sp<Tuner> tuner) {
|
||||
mDemuxId = demuxId;
|
||||
mTunerService = tuner;
|
||||
@@ -93,8 +54,8 @@ Return<Result> Demux::setFrontendDataSource(uint32_t frontendId) {
|
||||
return startBroadcastInputLoop();
|
||||
}
|
||||
|
||||
Return<void> Demux::addFilter(DemuxFilterType type, uint32_t bufferSize,
|
||||
const sp<IDemuxCallback>& cb, addFilter_cb _hidl_cb) {
|
||||
Return<void> Demux::openFilter(const DemuxFilterType& type, uint32_t bufferSize,
|
||||
const sp<IFilterCallback>& cb, openFilter_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
uint32_t filterId;
|
||||
@@ -105,137 +66,39 @@ Return<void> Demux::addFilter(DemuxFilterType type, uint32_t bufferSize,
|
||||
mUnusedFilterIds.erase(filterId);
|
||||
} else {
|
||||
filterId = ++mLastUsedFilterId;
|
||||
|
||||
mFilterCallbacks.resize(filterId + 1);
|
||||
mFilterMQs.resize(filterId + 1);
|
||||
mFilterEvents.resize(filterId + 1);
|
||||
mFilterEventFlags.resize(filterId + 1);
|
||||
mFilterThreadRunning.resize(filterId + 1);
|
||||
mFilterThreads.resize(filterId + 1);
|
||||
mFilterPids.resize(filterId + 1);
|
||||
mFilterOutputs.resize(filterId + 1);
|
||||
mFilterStatus.resize(filterId + 1);
|
||||
}
|
||||
|
||||
mUsedFilterIds.insert(filterId);
|
||||
|
||||
if ((type != DemuxFilterType::PCR || type != DemuxFilterType::TS) && cb == nullptr) {
|
||||
if (cb == nullptr) {
|
||||
ALOGW("callback can't be null");
|
||||
_hidl_cb(Result::INVALID_ARGUMENT, filterId);
|
||||
_hidl_cb(Result::INVALID_ARGUMENT, new Filter());
|
||||
return Void();
|
||||
}
|
||||
|
||||
// Add callback
|
||||
mFilterCallbacks[filterId] = cb;
|
||||
sp<Filter> filter = new Filter(type, filterId, bufferSize, cb, this);
|
||||
|
||||
// Mapping from the filter ID to the filter event
|
||||
DemuxFilterEvent event{
|
||||
.filterId = filterId,
|
||||
.filterType = type,
|
||||
};
|
||||
mFilterEvents[filterId] = event;
|
||||
|
||||
if (!createFilterMQ(bufferSize, filterId)) {
|
||||
_hidl_cb(Result::UNKNOWN_ERROR, -1);
|
||||
if (!filter->createFilterMQ()) {
|
||||
_hidl_cb(Result::UNKNOWN_ERROR, filter);
|
||||
return Void();
|
||||
}
|
||||
|
||||
_hidl_cb(Result::SUCCESS, filterId);
|
||||
mFilters[filterId] = filter;
|
||||
|
||||
_hidl_cb(Result::SUCCESS, filter);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> Demux::getFilterQueueDesc(uint32_t filterId, getFilterQueueDesc_cb _hidl_cb) {
|
||||
Return<void> Demux::openTimeFilter(openTimeFilter_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
if (mUsedFilterIds.find(filterId) == mUsedFilterIds.end()) {
|
||||
ALOGW("No filter with id: %d exists to get desc", filterId);
|
||||
_hidl_cb(Result::INVALID_ARGUMENT, FilterMQ::Descriptor());
|
||||
return Void();
|
||||
}
|
||||
sp<TimeFilter> timeFilter = new TimeFilter(this);
|
||||
|
||||
_hidl_cb(Result::SUCCESS, *mFilterMQs[filterId]->getDesc());
|
||||
_hidl_cb(Result::SUCCESS, timeFilter);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Demux::configureFilter(uint32_t filterId, const DemuxFilterSettings& settings) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
switch (mFilterEvents[filterId].filterType) {
|
||||
case DemuxFilterType::SECTION:
|
||||
mFilterPids[filterId] = settings.section().tpid;
|
||||
break;
|
||||
case DemuxFilterType::PES:
|
||||
mFilterPids[filterId] = settings.pesData().tpid;
|
||||
break;
|
||||
case DemuxFilterType::TS:
|
||||
mFilterPids[filterId] = settings.ts().tpid;
|
||||
break;
|
||||
case DemuxFilterType::AUDIO:
|
||||
mFilterPids[filterId] = settings.audio().tpid;
|
||||
break;
|
||||
case DemuxFilterType::VIDEO:
|
||||
mFilterPids[filterId] = settings.video().tpid;
|
||||
break;
|
||||
case DemuxFilterType::RECORD:
|
||||
mFilterPids[filterId] = settings.record().tpid;
|
||||
break;
|
||||
case DemuxFilterType::PCR:
|
||||
mFilterPids[filterId] = settings.pcr().tpid;
|
||||
break;
|
||||
default:
|
||||
return Result::UNKNOWN_ERROR;
|
||||
}
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Demux::startFilter(uint32_t filterId) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
Result result;
|
||||
|
||||
if (mUsedFilterIds.find(filterId) == mUsedFilterIds.end()) {
|
||||
ALOGW("No filter with id: %d exists to start filter", filterId);
|
||||
return Result::INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
result = startFilterLoop(filterId);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Return<Result> Demux::stopFilter(uint32_t filterId) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
mFilterThreadRunning[filterId] = false;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mFilterThreadLock);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Demux::flushFilter(uint32_t filterId) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
// temp implementation to flush the FMQ
|
||||
int size = mFilterMQs[filterId]->availableToRead();
|
||||
char* buffer = new char[size];
|
||||
mOutputMQ->read((unsigned char*)&buffer[0], size);
|
||||
delete[] buffer;
|
||||
mFilterStatus[filterId] = DemuxFilterStatus::DATA_READY;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Demux::removeFilter(uint32_t filterId) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
// resetFilterRecords(filterId);
|
||||
mUsedFilterIds.erase(filterId);
|
||||
mUnusedFilterIds.insert(filterId);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<void> Demux::getAvSyncHwId(uint32_t /* filterId */, getAvSyncHwId_cb _hidl_cb) {
|
||||
Return<void> Demux::getAvSyncHwId(const sp<IFilter>& /* filter */, getAvSyncHwId_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
AvSyncHwId avSyncHwId = 0;
|
||||
@@ -256,588 +119,81 @@ Return<void> Demux::getAvSyncTime(AvSyncHwId /* avSyncHwId */, getAvSyncTime_cb
|
||||
Return<Result> Demux::close() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
set<uint32_t>::iterator it;
|
||||
mInputThread = 0;
|
||||
mOutputThread = 0;
|
||||
mFilterThreads.clear();
|
||||
mUnusedFilterIds.clear();
|
||||
mUsedFilterIds.clear();
|
||||
mFilterCallbacks.clear();
|
||||
mFilterMQs.clear();
|
||||
mFilterEvents.clear();
|
||||
mFilterEventFlags.clear();
|
||||
mFilterOutputs.clear();
|
||||
mFilterPids.clear();
|
||||
mLastUsedFilterId = -1;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Demux::addOutput(uint32_t bufferSize, const sp<IDemuxCallback>& cb) {
|
||||
Return<void> Demux::openDvr(DvrType type, uint32_t bufferSize, const sp<IDvrCallback>& cb,
|
||||
openDvr_cb _hidl_cb) {
|
||||
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(bufferSize, true));
|
||||
if (!tmpFilterMQ->isValid()) {
|
||||
ALOGW("Failed to create output FMQ");
|
||||
return Result::UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mOutputMQ = std::move(tmpFilterMQ);
|
||||
|
||||
if (EventFlag::createEventFlag(mOutputMQ->getEventFlagWord(), &mOutputEventFlag) != OK) {
|
||||
return Result::UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mOutputCallback = cb;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<void> Demux::getOutputQueueDesc(getOutputQueueDesc_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
if (!mOutputMQ) {
|
||||
_hidl_cb(Result::NOT_INITIALIZED, FilterMQ::Descriptor());
|
||||
if (cb == nullptr) {
|
||||
ALOGW("DVR callback can't be null");
|
||||
_hidl_cb(Result::INVALID_ARGUMENT, new Dvr());
|
||||
return Void();
|
||||
}
|
||||
|
||||
_hidl_cb(Result::SUCCESS, *mOutputMQ->getDesc());
|
||||
return Void();
|
||||
}
|
||||
sp<Dvr> dvr = new Dvr(type, bufferSize, cb, this);
|
||||
|
||||
Return<Result> Demux::configureOutput(const DemuxOutputSettings& settings) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
mOutputConfigured = true;
|
||||
mOutputSettings = settings;
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Demux::attachOutputFilter(uint32_t /*filterId*/) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Demux::detachOutputFilter(uint32_t /* filterId */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Demux::startOutput() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Demux::stopOutput() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Demux::flushOutput() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Demux::removeOutput() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Demux::addInput(uint32_t bufferSize, const sp<IDemuxCallback>& cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
// Create a synchronized FMQ that supports blocking read/write
|
||||
std::unique_ptr<FilterMQ> tmpInputMQ =
|
||||
std::unique_ptr<FilterMQ>(new (std::nothrow) FilterMQ(bufferSize, true));
|
||||
if (!tmpInputMQ->isValid()) {
|
||||
ALOGW("Failed to create input FMQ");
|
||||
return Result::UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mInputMQ = std::move(tmpInputMQ);
|
||||
|
||||
if (EventFlag::createEventFlag(mInputMQ->getEventFlagWord(), &mInputEventFlag) != OK) {
|
||||
return Result::UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mInputCallback = cb;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<void> Demux::getInputQueueDesc(getInputQueueDesc_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
if (!mInputMQ) {
|
||||
_hidl_cb(Result::NOT_INITIALIZED, FilterMQ::Descriptor());
|
||||
if (!dvr->createDvrMQ()) {
|
||||
_hidl_cb(Result::UNKNOWN_ERROR, dvr);
|
||||
return Void();
|
||||
}
|
||||
|
||||
_hidl_cb(Result::SUCCESS, *mInputMQ->getDesc());
|
||||
_hidl_cb(Result::SUCCESS, dvr);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Demux::configureInput(const DemuxInputSettings& settings) {
|
||||
Result Demux::removeFilter(uint32_t filterId) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
mInputConfigured = true;
|
||||
mInputSettings = settings;
|
||||
// resetFilterRecords(filterId);
|
||||
mUsedFilterIds.erase(filterId);
|
||||
mUnusedFilterIds.insert(filterId);
|
||||
mFilters.erase(filterId);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Demux::startInput() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
if (!mInputCallback) {
|
||||
return Result::NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!mInputConfigured) {
|
||||
return Result::INVALID_STATE;
|
||||
}
|
||||
|
||||
pthread_create(&mInputThread, NULL, __threadLoopInput, this);
|
||||
pthread_setname_np(mInputThread, "demux_input_waiting_loop");
|
||||
|
||||
// TODO start another thread to send filter status callback to the framework
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Demux::stopInput() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
mInputThreadRunning = false;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mInputThreadLock);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Demux::flushInput() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Demux::removeInput() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
mInputMQ = nullptr;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Result Demux::startFilterLoop(uint32_t filterId) {
|
||||
struct ThreadArgs* threadArgs = (struct ThreadArgs*)malloc(sizeof(struct ThreadArgs));
|
||||
threadArgs->user = this;
|
||||
threadArgs->filterId = filterId;
|
||||
|
||||
pthread_t mFilterThread;
|
||||
pthread_create(&mFilterThread, NULL, __threadLoopFilter, (void*)threadArgs);
|
||||
mFilterThreads[filterId] = mFilterThread;
|
||||
pthread_setname_np(mFilterThread, "demux_filter_waiting_loop");
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Result Demux::startSectionFilterHandler(uint32_t filterId) {
|
||||
if (mFilterOutputs[filterId].empty()) {
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
if (!writeSectionsAndCreateEvent(filterId, mFilterOutputs[filterId])) {
|
||||
ALOGD("[Demux] filter %d fails to write into FMQ. Ending thread", filterId);
|
||||
return Result::UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mFilterOutputs[filterId].clear();
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Result Demux::startPesFilterHandler(uint32_t filterId) {
|
||||
std::lock_guard<std::mutex> lock(mFilterEventLock);
|
||||
if (mFilterOutputs[filterId].empty()) {
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
for (int i = 0; i < mFilterOutputs[filterId].size(); i += 188) {
|
||||
if (mPesSizeLeft == 0) {
|
||||
uint32_t prefix = (mFilterOutputs[filterId][i + 4] << 16) |
|
||||
(mFilterOutputs[filterId][i + 5] << 8) |
|
||||
mFilterOutputs[filterId][i + 6];
|
||||
ALOGD("[Demux] prefix %d", prefix);
|
||||
if (prefix == 0x000001) {
|
||||
// TODO handle mulptiple Pes filters
|
||||
mPesSizeLeft =
|
||||
(mFilterOutputs[filterId][i + 8] << 8) | mFilterOutputs[filterId][i + 9];
|
||||
mPesSizeLeft += 6;
|
||||
ALOGD("[Demux] pes data length %d", mPesSizeLeft);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
int endPoint = min(184, mPesSizeLeft);
|
||||
// append data and check size
|
||||
vector<uint8_t>::const_iterator first = mFilterOutputs[filterId].begin() + i + 4;
|
||||
vector<uint8_t>::const_iterator last = mFilterOutputs[filterId].begin() + i + 4 + endPoint;
|
||||
mPesOutput.insert(mPesOutput.end(), first, last);
|
||||
// size does not match then continue
|
||||
mPesSizeLeft -= endPoint;
|
||||
if (mPesSizeLeft > 0) {
|
||||
continue;
|
||||
}
|
||||
// size match then create event
|
||||
if (!writeDataToFilterMQ(mPesOutput, filterId)) {
|
||||
mFilterOutputs[filterId].clear();
|
||||
return Result::INVALID_STATE;
|
||||
}
|
||||
maySendFilterStatusCallback(filterId);
|
||||
DemuxFilterPesEvent pesEvent;
|
||||
pesEvent = {
|
||||
// temp dump meta data
|
||||
.streamId = mPesOutput[3],
|
||||
.dataLength = static_cast<uint16_t>(mPesOutput.size()),
|
||||
};
|
||||
ALOGD("[Demux] assembled pes data length %d", pesEvent.dataLength);
|
||||
|
||||
int size = mFilterEvents[filterId].events.size();
|
||||
mFilterEvents[filterId].events.resize(size + 1);
|
||||
mFilterEvents[filterId].events[size].pes(pesEvent);
|
||||
mPesOutput.clear();
|
||||
}
|
||||
|
||||
mFilterOutputs[filterId].clear();
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Result Demux::startTsFilterHandler() {
|
||||
// TODO handle starting TS filter
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Result Demux::startMediaFilterHandler(uint32_t filterId) {
|
||||
DemuxFilterMediaEvent mediaEvent;
|
||||
mediaEvent = {
|
||||
// temp dump meta data
|
||||
.pts = 0,
|
||||
.dataLength = 530,
|
||||
.secureMemory = nullptr,
|
||||
};
|
||||
mFilterEvents[filterId].events.resize(1);
|
||||
mFilterEvents[filterId].events[0].media() = mediaEvent;
|
||||
|
||||
mFilterOutputs[filterId].clear();
|
||||
// TODO handle write FQM for media stream
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Result Demux::startRecordFilterHandler(uint32_t filterId) {
|
||||
DemuxFilterRecordEvent recordEvent;
|
||||
recordEvent = {
|
||||
// temp dump meta data
|
||||
.tpid = 0,
|
||||
.packetNum = 0,
|
||||
};
|
||||
recordEvent.indexMask.tsIndexMask() = 0x01;
|
||||
mFilterEvents[filterId].events.resize(1);
|
||||
mFilterEvents[filterId].events[0].ts() = recordEvent;
|
||||
|
||||
mFilterOutputs[filterId].clear();
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Result Demux::startPcrFilterHandler() {
|
||||
// TODO handle starting PCR filter
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
bool Demux::createFilterMQ(uint32_t bufferSize, uint32_t filterId) {
|
||||
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(bufferSize, true));
|
||||
if (!tmpFilterMQ->isValid()) {
|
||||
ALOGW("Failed to create FMQ of filter with id: %d", filterId);
|
||||
return false;
|
||||
}
|
||||
|
||||
mFilterMQs[filterId] = std::move(tmpFilterMQ);
|
||||
|
||||
EventFlag* filterEventFlag;
|
||||
if (EventFlag::createEventFlag(mFilterMQs[filterId]->getEventFlagWord(), &filterEventFlag) !=
|
||||
OK) {
|
||||
return false;
|
||||
}
|
||||
mFilterEventFlags[filterId] = filterEventFlag;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Demux::writeSectionsAndCreateEvent(uint32_t filterId, vector<uint8_t> data) {
|
||||
// TODO check how many sections has been read
|
||||
std::lock_guard<std::mutex> lock(mFilterEventLock);
|
||||
if (!writeDataToFilterMQ(data, filterId)) {
|
||||
return false;
|
||||
}
|
||||
int size = mFilterEvents[filterId].events.size();
|
||||
mFilterEvents[filterId].events.resize(size + 1);
|
||||
DemuxFilterSectionEvent secEvent;
|
||||
secEvent = {
|
||||
// temp dump meta data
|
||||
.tableId = 0,
|
||||
.version = 1,
|
||||
.sectionNum = 1,
|
||||
.dataLength = static_cast<uint16_t>(data.size()),
|
||||
};
|
||||
mFilterEvents[filterId].events[size].section(secEvent);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Demux::writeDataToFilterMQ(const std::vector<uint8_t>& data, uint32_t filterId) {
|
||||
std::lock_guard<std::mutex> lock(mWriteLock);
|
||||
if (mFilterMQs[filterId]->write(data.data(), data.size())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Demux::readInputFMQ() {
|
||||
// Read input data from the input FMQ
|
||||
int size = mInputMQ->availableToRead();
|
||||
int inputPacketSize = mInputSettings.packetSize;
|
||||
vector<uint8_t> dataOutputBuffer;
|
||||
dataOutputBuffer.resize(inputPacketSize);
|
||||
|
||||
// Dispatch the packet to the PID matching filter output buffer
|
||||
for (int i = 0; i < size / inputPacketSize; i++) {
|
||||
if (!mInputMQ->read(dataOutputBuffer.data(), inputPacketSize)) {
|
||||
return false;
|
||||
}
|
||||
startTsFilter(dataOutputBuffer);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Demux::startTsFilter(vector<uint8_t> data) {
|
||||
set<uint32_t>::iterator it;
|
||||
for (it = mUsedFilterIds.begin(); it != mUsedFilterIds.end(); it++) {
|
||||
uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff));
|
||||
ALOGW("start ts filter pid: %d", pid);
|
||||
if (pid == mFilterPids[*it]) {
|
||||
mFilterOutputs[*it].insert(mFilterOutputs[*it].end(), data.begin(), data.end());
|
||||
if (DEBUG_FILTER) {
|
||||
ALOGW("start ts filter pid: %d", pid);
|
||||
}
|
||||
if (pid == mFilters[*it]->getTpid()) {
|
||||
mFilters[*it]->updateFilterOutput(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Demux::startFilterDispatcher() {
|
||||
Result result;
|
||||
set<uint32_t>::iterator it;
|
||||
|
||||
// Handle the output data per filter type
|
||||
for (it = mUsedFilterIds.begin(); it != mUsedFilterIds.end(); it++) {
|
||||
switch (mFilterEvents[*it].filterType) {
|
||||
case DemuxFilterType::SECTION:
|
||||
result = startSectionFilterHandler(*it);
|
||||
break;
|
||||
case DemuxFilterType::PES:
|
||||
result = startPesFilterHandler(*it);
|
||||
break;
|
||||
case DemuxFilterType::TS:
|
||||
result = startTsFilterHandler();
|
||||
break;
|
||||
case DemuxFilterType::AUDIO:
|
||||
case DemuxFilterType::VIDEO:
|
||||
result = startMediaFilterHandler(*it);
|
||||
break;
|
||||
case DemuxFilterType::RECORD:
|
||||
result = startRecordFilterHandler(*it);
|
||||
break;
|
||||
case DemuxFilterType::PCR:
|
||||
result = startPcrFilterHandler();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
if (mFilters[*it]->startFilterHandler() != Result::SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return result == Result::SUCCESS;
|
||||
return true;
|
||||
}
|
||||
|
||||
void* Demux::__threadLoopFilter(void* threadArg) {
|
||||
Demux* const self = static_cast<Demux*>(((struct ThreadArgs*)threadArg)->user);
|
||||
self->filterThreadLoop(((struct ThreadArgs*)threadArg)->filterId);
|
||||
return 0;
|
||||
Result Demux::startFilterHandler(uint32_t filterId) {
|
||||
return mFilters[filterId]->startFilterHandler();
|
||||
}
|
||||
|
||||
void* Demux::__threadLoopInput(void* user) {
|
||||
Demux* const self = static_cast<Demux*>(user);
|
||||
self->inputThreadLoop();
|
||||
return 0;
|
||||
void Demux::updateFilterOutput(uint16_t filterId, vector<uint8_t> data) {
|
||||
mFilters[filterId]->updateFilterOutput(data);
|
||||
}
|
||||
|
||||
void Demux::filterThreadLoop(uint32_t filterId) {
|
||||
ALOGD("[Demux] filter %d threadLoop start.", filterId);
|
||||
std::lock_guard<std::mutex> lock(mFilterThreadLock);
|
||||
mFilterThreadRunning[filterId] = 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[filterId]) {
|
||||
if (mFilterEvents[filterId].events.size() == 0) {
|
||||
ALOGD("[Demux] wait for filter data output.");
|
||||
usleep(1000 * 1000);
|
||||
continue;
|
||||
}
|
||||
// After successfully write, send a callback and wait for the read to be done
|
||||
mFilterCallbacks[filterId]->onFilterEvent(mFilterEvents[filterId]);
|
||||
mFilterEvents[filterId].events.resize(0);
|
||||
mFilterStatus[filterId] = DemuxFilterStatus::DATA_READY;
|
||||
mFilterCallbacks[filterId]->onFilterStatus(filterId, mFilterStatus[filterId]);
|
||||
break;
|
||||
}
|
||||
|
||||
while (mFilterThreadRunning[filterId]) {
|
||||
uint32_t efState = 0;
|
||||
// We do not wait for the last round of writen data to be read to finish the thread
|
||||
// because the VTS can verify the reading itself.
|
||||
for (int i = 0; i < SECTION_WRITE_COUNT; i++) {
|
||||
while (mFilterThreadRunning[filterId]) {
|
||||
status_t status = mFilterEventFlags[filterId]->wait(
|
||||
static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED), &efState,
|
||||
WAIT_TIMEOUT, true /* retry on spurious wake */);
|
||||
if (status != OK) {
|
||||
ALOGD("[Demux] wait for data consumed");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (mFilterCallbacks[filterId] == nullptr) {
|
||||
ALOGD("[Demux] filter %d does not hava callback. Ending thread", filterId);
|
||||
break;
|
||||
}
|
||||
|
||||
maySendFilterStatusCallback(filterId);
|
||||
|
||||
while (mFilterThreadRunning[filterId]) {
|
||||
std::lock_guard<std::mutex> lock(mFilterEventLock);
|
||||
if (mFilterEvents[filterId].events.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
// After successfully write, send a callback and wait for the read to be done
|
||||
mFilterCallbacks[filterId]->onFilterEvent(mFilterEvents[filterId]);
|
||||
mFilterEvents[filterId].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("[Demux] filter %d writing done. Ending thread", filterId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mFilterThreadRunning[filterId] = false;
|
||||
}
|
||||
|
||||
ALOGD("[Demux] filter thread ended.");
|
||||
}
|
||||
|
||||
void Demux::inputThreadLoop() {
|
||||
ALOGD("[Demux] input threadLoop start.");
|
||||
std::lock_guard<std::mutex> lock(mInputThreadLock);
|
||||
mInputThreadRunning = true;
|
||||
|
||||
while (mInputThreadRunning) {
|
||||
uint32_t efState = 0;
|
||||
status_t status =
|
||||
mInputEventFlag->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 input FMQ");
|
||||
continue;
|
||||
}
|
||||
// Our current implementation filter the data and write it into the filter FMQ immedaitely
|
||||
// after the DATA_READY from the VTS/framework
|
||||
if (!readInputFMQ() || !startFilterDispatcher()) {
|
||||
ALOGD("[Demux] input data failed to be filtered. Ending thread");
|
||||
break;
|
||||
}
|
||||
|
||||
maySendInputStatusCallback();
|
||||
}
|
||||
|
||||
mInputThreadRunning = false;
|
||||
ALOGD("[Demux] input thread ended.");
|
||||
}
|
||||
|
||||
void Demux::maySendInputStatusCallback() {
|
||||
std::lock_guard<std::mutex> lock(mInputStatusLock);
|
||||
int availableToRead = mInputMQ->availableToRead();
|
||||
int availableToWrite = mInputMQ->availableToWrite();
|
||||
|
||||
DemuxInputStatus newStatus =
|
||||
checkInputStatusChange(availableToWrite, availableToRead, mInputSettings.highThreshold,
|
||||
mInputSettings.lowThreshold);
|
||||
if (mIntputStatus != newStatus) {
|
||||
mInputCallback->onInputStatus(newStatus);
|
||||
mIntputStatus = newStatus;
|
||||
}
|
||||
}
|
||||
|
||||
void Demux::maySendFilterStatusCallback(uint32_t filterId) {
|
||||
std::lock_guard<std::mutex> lock(mFilterStatusLock);
|
||||
int availableToRead = mFilterMQs[filterId]->availableToRead();
|
||||
int availableToWrite = mFilterMQs[filterId]->availableToWrite();
|
||||
int fmqSize = mFilterMQs[filterId]->getQuantumCount();
|
||||
|
||||
DemuxFilterStatus newStatus =
|
||||
checkFilterStatusChange(filterId, availableToWrite, availableToRead,
|
||||
ceil(fmqSize * 0.75), ceil(fmqSize * 0.25));
|
||||
if (mFilterStatus[filterId] != newStatus) {
|
||||
mFilterCallbacks[filterId]->onFilterStatus(filterId, newStatus);
|
||||
mFilterStatus[filterId] = newStatus;
|
||||
}
|
||||
}
|
||||
|
||||
DemuxInputStatus Demux::checkInputStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
|
||||
uint32_t highThreshold, uint32_t lowThreshold) {
|
||||
if (availableToWrite == 0) {
|
||||
return DemuxInputStatus::SPACE_FULL;
|
||||
} else if (availableToRead > highThreshold) {
|
||||
return DemuxInputStatus::SPACE_ALMOST_FULL;
|
||||
} else if (availableToRead < lowThreshold) {
|
||||
return DemuxInputStatus::SPACE_ALMOST_EMPTY;
|
||||
} else if (availableToRead == 0) {
|
||||
return DemuxInputStatus::SPACE_EMPTY;
|
||||
}
|
||||
return mIntputStatus;
|
||||
}
|
||||
|
||||
DemuxFilterStatus Demux::checkFilterStatusChange(uint32_t filterId, 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[filterId];
|
||||
uint16_t Demux::getFilterTpid(uint32_t filterId) {
|
||||
return mFilters[filterId]->getTpid();
|
||||
}
|
||||
|
||||
Result Demux::startBroadcastInputLoop() {
|
||||
@@ -876,6 +232,7 @@ void Demux::broadcastInputThreadLoop() {
|
||||
for (int i = 0; i < writePacketAmount; i++) {
|
||||
inputData.read(buffer, packetSize);
|
||||
if (!inputData) {
|
||||
mKeepFetchingDataFromFrontend = false;
|
||||
mBroadcastInputThreadRunning = false;
|
||||
break;
|
||||
}
|
||||
@@ -888,7 +245,7 @@ void Demux::broadcastInputThreadLoop() {
|
||||
startTsFilter(byteBuffer);
|
||||
}
|
||||
startFilterDispatcher();
|
||||
sleep(1);
|
||||
usleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -898,6 +255,7 @@ void Demux::broadcastInputThreadLoop() {
|
||||
}
|
||||
|
||||
void Demux::stopBroadcastInput() {
|
||||
ALOGD("[Demux] stop frontend on demux");
|
||||
mKeepFetchingDataFromFrontend = false;
|
||||
mBroadcastInputThreadRunning = false;
|
||||
std::lock_guard<std::mutex> lock(mBroadcastInputThreadLock);
|
||||
|
||||
@@ -21,7 +21,10 @@
|
||||
#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;
|
||||
@@ -38,13 +41,17 @@ using ::android::hardware::kSynchronizedReadWrite;
|
||||
using ::android::hardware::MessageQueue;
|
||||
using ::android::hardware::MQDescriptorSync;
|
||||
using ::android::hardware::tv::tuner::V1_0::IDemux;
|
||||
using ::android::hardware::tv::tuner::V1_0::IDemuxCallback;
|
||||
using ::android::hardware::tv::tuner::V1_0::IDvrCallback;
|
||||
using ::android::hardware::tv::tuner::V1_0::IFilterCallback;
|
||||
using ::android::hardware::tv::tuner::V1_0::Result;
|
||||
|
||||
using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
|
||||
|
||||
class Tuner;
|
||||
class Dvr;
|
||||
class Filter;
|
||||
class Frontend;
|
||||
class TimeFilter;
|
||||
class Tuner;
|
||||
|
||||
class Demux : public IDemux {
|
||||
public:
|
||||
@@ -54,63 +61,27 @@ class Demux : public IDemux {
|
||||
|
||||
virtual Return<Result> setFrontendDataSource(uint32_t frontendId) override;
|
||||
|
||||
virtual Return<Result> close() override;
|
||||
virtual Return<void> openFilter(const DemuxFilterType& type, uint32_t bufferSize,
|
||||
const sp<IFilterCallback>& cb, openFilter_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<void> addFilter(DemuxFilterType type, uint32_t bufferSize,
|
||||
const sp<IDemuxCallback>& cb, addFilter_cb _hidl_cb) override;
|
||||
virtual Return<void> openTimeFilter(openTimeFilter_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<void> getFilterQueueDesc(uint32_t filterId,
|
||||
getFilterQueueDesc_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<Result> configureFilter(uint32_t filterId,
|
||||
const DemuxFilterSettings& settings) override;
|
||||
|
||||
virtual Return<Result> startFilter(uint32_t filterId) override;
|
||||
|
||||
virtual Return<Result> stopFilter(uint32_t filterId) override;
|
||||
|
||||
virtual Return<Result> flushFilter(uint32_t filterId) override;
|
||||
|
||||
virtual Return<Result> removeFilter(uint32_t filterId) override;
|
||||
|
||||
virtual Return<void> getAvSyncHwId(uint32_t filterId, getAvSyncHwId_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> addInput(uint32_t bufferSize, const sp<IDemuxCallback>& cb) override;
|
||||
virtual Return<Result> close() override;
|
||||
|
||||
virtual Return<void> getInputQueueDesc(getInputQueueDesc_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<Result> configureInput(const DemuxInputSettings& settings) override;
|
||||
|
||||
virtual Return<Result> startInput() override;
|
||||
|
||||
virtual Return<Result> stopInput() override;
|
||||
|
||||
virtual Return<Result> flushInput() override;
|
||||
|
||||
virtual Return<Result> removeInput() override;
|
||||
|
||||
virtual Return<Result> addOutput(uint32_t bufferSize, const sp<IDemuxCallback>& cb) override;
|
||||
|
||||
virtual Return<void> getOutputQueueDesc(getOutputQueueDesc_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<Result> configureOutput(const DemuxOutputSettings& settings) override;
|
||||
|
||||
virtual Return<Result> attachOutputFilter(uint32_t filterId) override;
|
||||
|
||||
virtual Return<Result> detachOutputFilter(uint32_t filterId) override;
|
||||
|
||||
virtual Return<Result> startOutput() override;
|
||||
|
||||
virtual Return<Result> stopOutput() override;
|
||||
|
||||
virtual Return<Result> flushOutput() override;
|
||||
|
||||
virtual Return<Result> removeOutput() override;
|
||||
virtual Return<void> openDvr(DvrType type, uint32_t bufferSize, const sp<IDvrCallback>& cb,
|
||||
openDvr_cb _hidl_cb) override;
|
||||
|
||||
// Functions interacts with Tuner Service
|
||||
void stopBroadcastInput();
|
||||
Result removeFilter(uint32_t filterId);
|
||||
Result startFilterHandler(uint32_t filterId);
|
||||
void updateFilterOutput(uint16_t filterId, vector<uint8_t> data);
|
||||
uint16_t getFilterTpid(uint32_t filterId);
|
||||
|
||||
private:
|
||||
// Tuner service
|
||||
@@ -126,19 +97,9 @@ class Demux : public IDemux {
|
||||
uint32_t filterId;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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(uint32_t filterId);
|
||||
Result startPesFilterHandler(uint32_t filterId);
|
||||
Result startTsFilterHandler();
|
||||
Result startMediaFilterHandler(uint32_t filterId);
|
||||
Result startRecordFilterHandler(uint32_t filterId);
|
||||
Result startPcrFilterHandler();
|
||||
Result startFilterLoop(uint32_t filterId);
|
||||
Result startBroadcastInputLoop();
|
||||
static void* __threadLoopBroadcast(void* user);
|
||||
void broadcastInputThreadLoop();
|
||||
|
||||
/**
|
||||
* To create a FilterMQ with the the next available Filter ID.
|
||||
@@ -147,32 +108,14 @@ class Demux : public IDemux {
|
||||
*
|
||||
* Return false is any of the above processes fails.
|
||||
*/
|
||||
bool createFilterMQ(uint32_t bufferSize, uint32_t filterId);
|
||||
bool createMQ(FilterMQ* queue, EventFlag* eventFlag, uint32_t bufferSize);
|
||||
void deleteEventFlag();
|
||||
bool writeDataToFilterMQ(const std::vector<uint8_t>& data, uint32_t filterId);
|
||||
bool readDataFromMQ();
|
||||
bool writeSectionsAndCreateEvent(uint32_t filterId, vector<uint8_t> data);
|
||||
void maySendInputStatusCallback();
|
||||
void maySendFilterStatusCallback(uint32_t filterId);
|
||||
DemuxInputStatus checkInputStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
|
||||
uint32_t highThreshold, uint32_t lowThreshold);
|
||||
DemuxFilterStatus checkFilterStatusChange(uint32_t filterId, 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.
|
||||
*/
|
||||
bool readInputFMQ();
|
||||
void startTsFilter(vector<uint8_t> data);
|
||||
bool startFilterDispatcher();
|
||||
static void* __threadLoopFilter(void* data);
|
||||
static void* __threadLoopInput(void* user);
|
||||
static void* __threadLoopBroadcast(void* user);
|
||||
void filterThreadLoop(uint32_t filterId);
|
||||
void inputThreadLoop();
|
||||
void broadcastInputThreadLoop();
|
||||
void startTsFilter(vector<uint8_t> data);
|
||||
|
||||
uint32_t mDemuxId;
|
||||
/**
|
||||
@@ -196,69 +139,30 @@ class Demux : public IDemux {
|
||||
* A list of created FilterMQ ptrs.
|
||||
* The array number is the filter ID.
|
||||
*/
|
||||
vector<uint16_t> mFilterPids;
|
||||
vector<vector<uint8_t>> mFilterOutputs;
|
||||
vector<unique_ptr<FilterMQ>> mFilterMQs;
|
||||
vector<EventFlag*> mFilterEventFlags;
|
||||
vector<DemuxFilterEvent> mFilterEvents;
|
||||
unique_ptr<FilterMQ> mInputMQ;
|
||||
unique_ptr<FilterMQ> mOutputMQ;
|
||||
EventFlag* mInputEventFlag;
|
||||
EventFlag* mOutputEventFlag;
|
||||
/**
|
||||
* Demux callbacks used on filter events or IO buffer status
|
||||
*/
|
||||
vector<sp<IDemuxCallback>> mFilterCallbacks;
|
||||
sp<IDemuxCallback> mInputCallback;
|
||||
sp<IDemuxCallback> mOutputCallback;
|
||||
bool mInputConfigured = false;
|
||||
bool mOutputConfigured = false;
|
||||
DemuxInputSettings mInputSettings;
|
||||
DemuxOutputSettings mOutputSettings;
|
||||
std::map<uint32_t, sp<Filter>> mFilters;
|
||||
|
||||
// Thread handlers
|
||||
pthread_t mInputThread;
|
||||
pthread_t mOutputThread;
|
||||
pthread_t mBroadcastInputThread;
|
||||
vector<pthread_t> mFilterThreads;
|
||||
|
||||
// FMQ status local records
|
||||
DemuxInputStatus mIntputStatus;
|
||||
vector<DemuxFilterStatus> mFilterStatus;
|
||||
/**
|
||||
* If a specific filter's writing loop is still running
|
||||
*/
|
||||
vector<bool> mFilterThreadRunning;
|
||||
bool mInputThreadRunning;
|
||||
bool mBroadcastInputThreadRunning;
|
||||
bool mKeepFetchingDataFromFrontend;
|
||||
/**
|
||||
* 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 mInputStatusLock;
|
||||
std::mutex mFilterStatusLock;
|
||||
std::mutex mBroadcastInputThreadLock;
|
||||
std::mutex mFilterThreadLock;
|
||||
std::mutex mInputThreadLock;
|
||||
/**
|
||||
* How many times a filter should write
|
||||
* TODO make this dynamic/random/can take as a parameter
|
||||
*/
|
||||
const uint16_t SECTION_WRITE_COUNT = 10;
|
||||
|
||||
// temp handle single PES filter
|
||||
// TODO handle mulptiple Pes filters
|
||||
int mPesSizeLeft = 0;
|
||||
vector<uint8_t> mPesOutput;
|
||||
|
||||
const bool DEBUG_FILTER = false;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
|
||||
@@ -50,13 +50,15 @@ Return<Result> Descrambler::setKeyToken(const hidl_vec<uint8_t>& /* keyToken */)
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Descrambler::addPid(uint16_t /* pid */) {
|
||||
Return<Result> Descrambler::addPid(const DemuxPid& /* pid */,
|
||||
const sp<IFilter>& /* optionalSourceFilter */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Descrambler::removePid(uint16_t /* pid */) {
|
||||
Return<Result> Descrambler::removePid(const DemuxPid& /* pid */,
|
||||
const sp<IFilter>& /* optionalSourceFilter */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
|
||||
@@ -40,9 +40,11 @@ class Descrambler : public IDescrambler {
|
||||
|
||||
virtual Return<Result> setKeyToken(const hidl_vec<uint8_t>& keyToken) override;
|
||||
|
||||
virtual Return<Result> addPid(uint16_t pid) override;
|
||||
virtual Return<Result> addPid(const DemuxPid& pid,
|
||||
const sp<IFilter>& optionalSourceFilter) override;
|
||||
|
||||
virtual Return<Result> removePid(uint16_t pid) override;
|
||||
virtual Return<Result> removePid(const DemuxPid& pid,
|
||||
const sp<IFilter>& optionalSourceFilter) override;
|
||||
|
||||
virtual Return<Result> close() override;
|
||||
|
||||
|
||||
280
tv/tuner/1.0/default/Dvr.cpp
Normal file
280
tv/tuner/1.0/default/Dvr.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "android.hardware.tv.tuner@1.0-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<IFilter>& filter) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
uint32_t filterId;
|
||||
Result status;
|
||||
|
||||
filter->getId([&](Result result, uint32_t id) {
|
||||
filterId = id;
|
||||
status = result;
|
||||
});
|
||||
|
||||
if (status != Result::SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
mFilters[filterId] = filter;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Dvr::detachFilter(const sp<IFilter>& filter) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
uint32_t filterId;
|
||||
Result status;
|
||||
|
||||
filter->getId([&](Result result, uint32_t id) {
|
||||
filterId = id;
|
||||
status = result;
|
||||
});
|
||||
|
||||
if (status != Result::SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
std::map<uint32_t, sp<IFilter>>::iterator it;
|
||||
|
||||
it = mFilters.find(filterId);
|
||||
if (it != mFilters.end()) {
|
||||
mFilters.erase(filterId);
|
||||
}
|
||||
|
||||
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) {
|
||||
/*pthread_create(&mInputThread, NULL, __threadLoopInput, this);
|
||||
pthread_setname_np(mInputThread, "playback_waiting_loop");*/
|
||||
}
|
||||
|
||||
// TODO start another thread to send filter status callback to the framework
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Dvr::stop() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
mDvrThreadRunning = false;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mDvrThreadLock);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Dvr::flush() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
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
|
||||
std::unique_ptr<DvrMQ> tmpDvrMQ =
|
||||
std::unique_ptr<DvrMQ>(new (std::nothrow) DvrMQ(mBufferSize, true));
|
||||
if (!tmpDvrMQ->isValid()) {
|
||||
ALOGW("Failed to create FMQ of DVR");
|
||||
return false;
|
||||
}
|
||||
|
||||
mDvrMQ = std::move(tmpDvrMQ);
|
||||
|
||||
if (EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrEventFlag) != OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void* Dvr::__threadLoopPlayback(void* user) {
|
||||
Dvr* const self = static_cast<Dvr*>(user);
|
||||
self->playbackThreadLoop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Dvr::playbackThreadLoop() {
|
||||
ALOGD("[Dvr] playback threadLoop start.");
|
||||
std::lock_guard<std::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;
|
||||
}
|
||||
// Our current implementation filter the data and write it into the filter FMQ immediately
|
||||
// after the DATA_READY from the VTS/framework
|
||||
if (!readPlaybackFMQ() || !startFilterDispatcher()) {
|
||||
ALOGD("[Dvr] playback data failed to be filtered. Ending thread");
|
||||
break;
|
||||
}
|
||||
|
||||
maySendPlaybackStatusCallback();
|
||||
}
|
||||
|
||||
mDvrThreadRunning = false;
|
||||
ALOGD("[Dvr] playback thread ended.");
|
||||
}
|
||||
|
||||
void Dvr::maySendPlaybackStatusCallback() {
|
||||
std::lock_guard<std::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() {
|
||||
// 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;
|
||||
}
|
||||
startTpidFilter(dataOutputBuffer);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Dvr::startTpidFilter(vector<uint8_t> data) {
|
||||
std::map<uint32_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() {
|
||||
std::map<uint32_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;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
151
tv/tuner/1.0/default/Dvr.h
Normal file
151
tv/tuner/1.0/default/Dvr.h
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_HARDWARE_TV_TUNER_V1_0_DVR_H_
|
||||
#define ANDROID_HARDWARE_TV_TUNER_V1_0_DVR_H_
|
||||
|
||||
#include <android/hardware/tv/tuner/1.0/IDvr.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 ::android::hardware::tv::tuner::V1_0::IDemux;
|
||||
using ::android::hardware::tv::tuner::V1_0::IDvrCallback;
|
||||
using ::android::hardware::tv::tuner::V1_0::Result;
|
||||
|
||||
using DvrMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
|
||||
|
||||
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();
|
||||
|
||||
private:
|
||||
// Demux service
|
||||
sp<Demux> mDemux;
|
||||
|
||||
DvrType mType;
|
||||
uint32_t mBufferSize;
|
||||
sp<IDvrCallback> mCallback;
|
||||
std::map<uint32_t, sp<IFilter>> mFilters;
|
||||
|
||||
void deleteEventFlag();
|
||||
bool readDataFromMQ();
|
||||
void maySendPlaybackStatusCallback();
|
||||
void maySendRecordStatusCallback();
|
||||
PlaybackStatus checkPlaybackStatusChange(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.
|
||||
*/
|
||||
bool readPlaybackFMQ();
|
||||
void startTpidFilter(vector<uint8_t> data);
|
||||
bool startFilterDispatcher();
|
||||
static void* __threadLoopPlayback(void* user);
|
||||
static void* __threadLoopBroadcast(void* user);
|
||||
void playbackThreadLoop();
|
||||
void broadcastInputThreadLoop();
|
||||
|
||||
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;
|
||||
pthread_t mBroadcastInputThread;
|
||||
|
||||
// FMQ status local records
|
||||
PlaybackStatus mPlaybackStatus;
|
||||
/**
|
||||
* If a specific filter's writing loop is still running
|
||||
*/
|
||||
bool mDvrThreadRunning;
|
||||
bool mBroadcastInputThreadRunning;
|
||||
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 mBroadcastInputThreadLock;
|
||||
std::mutex mDvrThreadLock;
|
||||
|
||||
const bool DEBUG_DVR = false;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_TV_TUNER_V1_0_DVR_H_
|
||||
456
tv/tuner/1.0/default/Filter.cpp
Normal file
456
tv/tuner/1.0/default/Filter.cpp
Normal file
@@ -0,0 +1,456 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "android.hardware.tv.tuner@1.0-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, uint32_t filterId, uint32_t bufferSize,
|
||||
const sp<IFilterCallback>& cb, sp<Demux> demux) {
|
||||
mType = type;
|
||||
mFilterId = filterId;
|
||||
mBufferSize = bufferSize;
|
||||
mCallback = cb;
|
||||
mDemux = demux;
|
||||
}
|
||||
|
||||
Filter::~Filter() {}
|
||||
|
||||
Return<void> Filter::getId(getId_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
_hidl_cb(Result::SUCCESS, mFilterId);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> Filter::setDataSource(const sp<IFilter>& filter) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
mDataSource = filter;
|
||||
mIsDataSourceDemux = false;
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<void> Filter::getQueueDesc(getQueueDesc_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
_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:
|
||||
/*mmtpSettings*/
|
||||
break;
|
||||
case DemuxFilterMainType::IP:
|
||||
/*ipSettings*/
|
||||
break;
|
||||
case DemuxFilterMainType::TLV:
|
||||
/*tlvSettings*/
|
||||
break;
|
||||
case DemuxFilterMainType::ALP:
|
||||
/*alpSettings*/
|
||||
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::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("Failed to create FMQ of filter with id: %d", 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 %d 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) {
|
||||
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);
|
||||
mFilterEvent.events.resize(0);
|
||||
mFilterStatus = DemuxFilterStatus::DATA_READY;
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (mCallback == nullptr) {
|
||||
ALOGD("[Filter] filter %d does not hava callback. Ending thread", mFilterId);
|
||||
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 %d writing done. Ending thread", mFilterId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mFilterThreadRunning = false;
|
||||
}
|
||||
|
||||
ALOGD("[Filter] filter thread ended.");
|
||||
}
|
||||
|
||||
void Filter::maySendFilterStatusCallback() {
|
||||
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);
|
||||
ALOGD("[Filter] handler output updated");
|
||||
mFilterOutput.insert(mFilterOutput.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::RECORD:
|
||||
startRecordFilterHandler();
|
||||
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 %d 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];
|
||||
ALOGD("[Filter] prefix %d", prefix);
|
||||
if (prefix == 0x000001) {
|
||||
// TODO handle mulptiple Pes filters
|
||||
mPesSizeLeft = (mFilterOutput[i + 8] << 8) | mFilterOutput[i + 9];
|
||||
mPesSizeLeft += 6;
|
||||
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;
|
||||
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()),
|
||||
};
|
||||
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() {
|
||||
DemuxFilterMediaEvent mediaEvent;
|
||||
mediaEvent = {
|
||||
// temp dump meta data
|
||||
.pts = 0,
|
||||
.dataLength = 530,
|
||||
.avMemory = nullptr,
|
||||
.isSecureMemory = false,
|
||||
};
|
||||
mFilterEvent.events.resize(1);
|
||||
mFilterEvent.events[0].media(mediaEvent);
|
||||
|
||||
mFilterOutput.clear();
|
||||
// TODO handle write FQM for media stream
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Result Filter::startRecordFilterHandler() {
|
||||
DemuxFilterTsRecordEvent tsRecordEvent;
|
||||
tsRecordEvent.pid.tPid(0);
|
||||
tsRecordEvent.indexMask.tsIndexMask(0x01);
|
||||
mFilterEvent.events.resize(1);
|
||||
mFilterEvent.events[0].tsRecord(tsRecordEvent);
|
||||
|
||||
mFilterOutput.clear();
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Result Filter::startPcrFilterHandler() {
|
||||
// TODO handle starting PCR filter
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
bool Filter::writeSectionsAndCreateEvent(vector<uint8_t> data) {
|
||||
// TODO check how many sections has been read
|
||||
ALOGD("[Filter] section hander");
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
179
tv/tuner/1.0/default/Filter.h
Normal file
179
tv/tuner/1.0/default/Filter.h
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_HARDWARE_TV_TUNER_V1_0_FILTER_H_
|
||||
#define ANDROID_HARDWARE_TV_TUNER_V1_0_FILTER_H_
|
||||
|
||||
#include <android/hardware/tv/tuner/1.0/IFilter.h>
|
||||
#include <fmq/MessageQueue.h>
|
||||
#include <math.h>
|
||||
#include <set>
|
||||
#include "Demux.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 ::android::hardware::tv::tuner::V1_0::IDemux;
|
||||
using ::android::hardware::tv::tuner::V1_0::IFilterCallback;
|
||||
using ::android::hardware::tv::tuner::V1_0::Result;
|
||||
|
||||
using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
|
||||
|
||||
class Demux;
|
||||
|
||||
class Filter : public IFilter {
|
||||
public:
|
||||
Filter();
|
||||
|
||||
Filter(DemuxFilterType type, uint32_t filterId, uint32_t bufferSize,
|
||||
const sp<IFilterCallback>& cb, sp<Demux> demux);
|
||||
|
||||
~Filter();
|
||||
|
||||
virtual Return<void> getId(getId_cb _hidl_cb) override;
|
||||
|
||||
virtual Return<Result> setDataSource(const sp<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> 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);
|
||||
Result startFilterHandler();
|
||||
|
||||
private:
|
||||
// Tuner service
|
||||
sp<Demux> mDemux;
|
||||
/**
|
||||
* Filter callbacks used on filter events or FMQ status
|
||||
*/
|
||||
sp<IFilterCallback> mCallback;
|
||||
|
||||
uint32_t mFilterId;
|
||||
uint32_t mBufferSize;
|
||||
DemuxFilterType mType;
|
||||
DemuxFilterSettings mFilterSettings;
|
||||
|
||||
uint16_t mTpid;
|
||||
sp<IFilter> mDataSource;
|
||||
bool mIsDataSourceDemux = true;
|
||||
vector<uint8_t> mFilterOutput;
|
||||
unique_ptr<FilterMQ> mFilterMQ;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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 startRecordFilterHandler();
|
||||
Result startPcrFilterHandler();
|
||||
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();
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
// temp handle single PES filter
|
||||
// TODO handle mulptiple Pes filters
|
||||
int mPesSizeLeft = 0;
|
||||
vector<uint8_t> mPesOutput;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_TV_TUNER_V1_0_FILTER_H_
|
||||
@@ -75,7 +75,7 @@ class Frontend : public IFrontend {
|
||||
FrontendType mType = FrontendType::UNDEFINED;
|
||||
FrontendId mId = 0;
|
||||
|
||||
const string FRONTEND_STREAM_FILE = "/vendor/etc/test1.ts";
|
||||
const string FRONTEND_STREAM_FILE = "/vendor/etc/dumpTs3.ts";
|
||||
string mSourceStreamFile;
|
||||
std::ifstream mFrontendData;
|
||||
};
|
||||
|
||||
@@ -30,19 +30,25 @@ Lnb::Lnb() {}
|
||||
|
||||
Lnb::~Lnb() {}
|
||||
|
||||
Return<Result> Lnb::setVoltage(FrontendLnbVoltage /* voltage */) {
|
||||
Return<Result> Lnb::setCallback(const sp<ILnbCallback>& /* callback */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Lnb::setTone(FrontendLnbTone /* tone */) {
|
||||
Return<Result> Lnb::setVoltage(LnbVoltage /* voltage */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Lnb::setSatellitePosition(FrontendLnbPosition /* position */) {
|
||||
Return<Result> Lnb::setTone(LnbTone /* tone */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> Lnb::setSatellitePosition(LnbPosition /* position */) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
|
||||
@@ -29,20 +29,23 @@ namespace tuner {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::hardware::tv::tuner::V1_0::FrontendLnbPosition;
|
||||
using ::android::hardware::tv::tuner::V1_0::FrontendLnbTone;
|
||||
using ::android::hardware::tv::tuner::V1_0::FrontendLnbVoltage;
|
||||
using ::android::hardware::tv::tuner::V1_0::ILnbCallback;
|
||||
using ::android::hardware::tv::tuner::V1_0::LnbPosition;
|
||||
using ::android::hardware::tv::tuner::V1_0::LnbTone;
|
||||
using ::android::hardware::tv::tuner::V1_0::LnbVoltage;
|
||||
using ::android::hardware::tv::tuner::V1_0::Result;
|
||||
|
||||
class Lnb : public ILnb {
|
||||
public:
|
||||
Lnb();
|
||||
|
||||
virtual Return<Result> setVoltage(FrontendLnbVoltage voltage) override;
|
||||
virtual Return<Result> setCallback(const sp<ILnbCallback>& callback) override;
|
||||
|
||||
virtual Return<Result> setTone(FrontendLnbTone tone) override;
|
||||
virtual Return<Result> setVoltage(LnbVoltage voltage) override;
|
||||
|
||||
virtual Return<Result> setSatellitePosition(FrontendLnbPosition position) 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;
|
||||
|
||||
|
||||
78
tv/tuner/1.0/default/TimeFilter.cpp
Normal file
78
tv/tuner/1.0/default/TimeFilter.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "android.hardware.tv.tuner@1.0-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__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<Result> TimeFilter::clearTimeStamp() {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
Return<void> TimeFilter::getTimeStamp(getTimeStamp_cb _hidl_cb) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
uint64_t timeStamp = 0;
|
||||
|
||||
_hidl_cb(Result::SUCCESS, timeStamp);
|
||||
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__);
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
69
tv/tuner/1.0/default/TimeFilter.h
Normal file
69
tv/tuner/1.0/default/TimeFilter.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_HARDWARE_TV_TUNER_V1_0_TIMEFILTER_H_
|
||||
#define ANDROID_HARDWARE_TV_TUNER_V1_0_TIMEFILTER_H_
|
||||
|
||||
#include <android/hardware/tv/tuner/1.0/ITimeFilter.h>
|
||||
#include "Demux.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace tv {
|
||||
namespace tuner {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::hardware::tv::tuner::V1_0::IDemux;
|
||||
using ::android::hardware::tv::tuner::V1_0::IFilterCallback;
|
||||
using ::android::hardware::tv::tuner::V1_0::Result;
|
||||
|
||||
using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace tuner
|
||||
} // namespace tv
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_TV_TUNER_V1_0_TIMEFILTER_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,8 +20,11 @@
|
||||
#include <VtsHalHidlTargetTestEnvBase.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android/hardware/tv/tuner/1.0/IDemux.h>
|
||||
#include <android/hardware/tv/tuner/1.0/IDemuxCallback.h>
|
||||
#include <android/hardware/tv/tuner/1.0/IDescrambler.h>
|
||||
#include <android/hardware/tv/tuner/1.0/IDvr.h>
|
||||
#include <android/hardware/tv/tuner/1.0/IDvrCallback.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/IFrontend.h>
|
||||
#include <android/hardware/tv/tuner/1.0/IFrontendCallback.h>
|
||||
#include <android/hardware/tv/tuner/1.0/ITuner.h>
|
||||
@@ -57,8 +60,9 @@ using android::hardware::MessageQueue;
|
||||
using android::hardware::MQDescriptorSync;
|
||||
using android::hardware::Return;
|
||||
using android::hardware::Void;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxDataFormat;
|
||||
using android::hardware::tv::tuner::V1_0::DataFormat;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterPesEvent;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxFilterSectionEvent;
|
||||
@@ -66,10 +70,11 @@ using android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings;
|
||||
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::DemuxInputSettings;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxInputStatus;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxOutputStatus;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
|
||||
using android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
|
||||
using android::hardware::tv::tuner::V1_0::DvrSettings;
|
||||
using android::hardware::tv::tuner::V1_0::DvrType;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendAtscModulation;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendAtscSettings;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendDvbtSettings;
|
||||
@@ -80,11 +85,17 @@ using android::hardware::tv::tuner::V1_0::FrontendScanMessage;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
|
||||
using android::hardware::tv::tuner::V1_0::FrontendSettings;
|
||||
using android::hardware::tv::tuner::V1_0::IDemux;
|
||||
using android::hardware::tv::tuner::V1_0::IDemuxCallback;
|
||||
using android::hardware::tv::tuner::V1_0::IDescrambler;
|
||||
using android::hardware::tv::tuner::V1_0::IDvr;
|
||||
using android::hardware::tv::tuner::V1_0::IDvrCallback;
|
||||
using android::hardware::tv::tuner::V1_0::IFilter;
|
||||
using android::hardware::tv::tuner::V1_0::IFilterCallback;
|
||||
using android::hardware::tv::tuner::V1_0::IFrontend;
|
||||
using android::hardware::tv::tuner::V1_0::IFrontendCallback;
|
||||
using android::hardware::tv::tuner::V1_0::ITuner;
|
||||
using android::hardware::tv::tuner::V1_0::PlaybackSettings;
|
||||
using android::hardware::tv::tuner::V1_0::PlaybackStatus;
|
||||
using android::hardware::tv::tuner::V1_0::RecordStatus;
|
||||
using android::hardware::tv::tuner::V1_0::Result;
|
||||
|
||||
namespace {
|
||||
@@ -131,17 +142,28 @@ const std::vector<uint8_t> goldenDataOutputBuffer{
|
||||
0x73, 0x63, 0x65, 0x6e, 0x65,
|
||||
};
|
||||
|
||||
const uint16_t FMQ_SIZE_4K = 0x1000;
|
||||
// const uint16_t FMQ_SIZE_4K = 0x1000;
|
||||
const uint32_t FMQ_SIZE_1M = 0x100000;
|
||||
const uint32_t FMQ_SIZE_16M = 0x1000000;
|
||||
|
||||
struct FilterConf {
|
||||
DemuxFilterType type;
|
||||
DemuxFilterSettings setting;
|
||||
};
|
||||
|
||||
struct InputConf {
|
||||
enum FilterEventType : uint8_t {
|
||||
UNDEFINED,
|
||||
SECTION,
|
||||
MEDIA,
|
||||
PES,
|
||||
RECORD,
|
||||
MMTPRECORD,
|
||||
DOWNLOAD,
|
||||
};
|
||||
|
||||
struct PlaybackConf {
|
||||
string inputDataFile;
|
||||
DemuxInputSettings setting;
|
||||
PlaybackSettings setting;
|
||||
};
|
||||
|
||||
class FrontendCallback : public IFrontendCallback {
|
||||
@@ -154,14 +176,6 @@ class FrontendCallback : public IFrontendCallback {
|
||||
return Void();
|
||||
}
|
||||
|
||||
virtual Return<void> onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) override {
|
||||
android::Mutex::Autolock autoLock(mMsgLock);
|
||||
mDiseqcMessageReceived = true;
|
||||
mEventMessage = diseqcMessage;
|
||||
mMsgCondition.signal();
|
||||
return Void();
|
||||
}
|
||||
|
||||
virtual Return<void> onScanMessage(FrontendScanMessageType /* type */,
|
||||
const FrontendScanMessage& /* message */) override {
|
||||
android::Mutex::Autolock autoLock(mMsgLock);
|
||||
@@ -211,14 +225,14 @@ void FrontendCallback::testOnDiseqcMessage(sp<IFrontend>& frontend, FrontendSett
|
||||
}
|
||||
}
|
||||
|
||||
class DemuxCallback : public IDemuxCallback {
|
||||
class FilterCallback : public IFilterCallback {
|
||||
public:
|
||||
virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent) override {
|
||||
android::Mutex::Autolock autoLock(mMsgLock);
|
||||
// Temprarily we treat the first coming back filter data on the matching pid a success
|
||||
// once all of the MQ are cleared, means we got all the expected output
|
||||
mFilterIdToEvent[filterEvent.filterId] = filterEvent;
|
||||
readFilterEventData(filterEvent.filterId);
|
||||
mFilterIdToEvent = filterEvent;
|
||||
readFilterEventData();
|
||||
mPidFilterOutputCount++;
|
||||
// mFilterIdToMQ.erase(filterEvent.filterId);
|
||||
|
||||
@@ -227,96 +241,50 @@ class DemuxCallback : public IDemuxCallback {
|
||||
return Void();
|
||||
}
|
||||
|
||||
virtual Return<void> onFilterStatus(uint32_t /*filterId*/,
|
||||
const DemuxFilterStatus /*status*/) override {
|
||||
virtual Return<void> onFilterStatus(const DemuxFilterStatus /*status*/) override {
|
||||
return Void();
|
||||
}
|
||||
|
||||
virtual Return<void> onOutputStatus(DemuxOutputStatus /*status*/) override { return Void(); }
|
||||
void setFilterId(uint32_t filterId) { mFilterId = filterId; }
|
||||
void setFilterEventType(FilterEventType type) { mFilterEventType = type; }
|
||||
|
||||
virtual Return<void> onInputStatus(DemuxInputStatus status) override {
|
||||
// android::Mutex::Autolock autoLock(mMsgLock);
|
||||
ALOGW("[vts] input status %d", status);
|
||||
switch (status) {
|
||||
case DemuxInputStatus::SPACE_EMPTY:
|
||||
case DemuxInputStatus::SPACE_ALMOST_EMPTY:
|
||||
ALOGW("[vts] keep inputing %d", status);
|
||||
mKeepWritingInputFMQ = true;
|
||||
break;
|
||||
case DemuxInputStatus::SPACE_ALMOST_FULL:
|
||||
case DemuxInputStatus::SPACE_FULL:
|
||||
ALOGW("[vts] stop inputing %d", status);
|
||||
mKeepWritingInputFMQ = false;
|
||||
break;
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
void testOnFilterEvent(uint32_t filterId);
|
||||
void testFilterDataOutput();
|
||||
void stopInputThread();
|
||||
|
||||
void startPlaybackInputThread(InputConf inputConf, MQDesc& inputMQDescriptor);
|
||||
void startFilterEventThread(DemuxFilterEvent event);
|
||||
static void* __threadLoopInput(void* threadArgs);
|
||||
static void* __threadLoopFilter(void* threadArgs);
|
||||
void inputThreadLoop(InputConf* inputConf, bool* keepWritingInputFMQ);
|
||||
void filterThreadLoop(DemuxFilterEvent& event);
|
||||
|
||||
void updateFilterMQ(uint32_t filterId, MQDesc& filterMQDescriptor);
|
||||
void updateGoldenOutputMap(uint32_t filterId, string goldenOutputFile);
|
||||
bool readFilterEventData(uint32_t filterId);
|
||||
void updateFilterMQ(MQDesc& filterMQDescriptor);
|
||||
void updateGoldenOutputMap(string goldenOutputFile);
|
||||
bool readFilterEventData();
|
||||
|
||||
private:
|
||||
struct InputThreadArgs {
|
||||
DemuxCallback* user;
|
||||
InputConf* inputConf;
|
||||
bool* keepWritingInputFMQ;
|
||||
};
|
||||
struct FilterThreadArgs {
|
||||
DemuxCallback* user;
|
||||
FilterCallback* user;
|
||||
DemuxFilterEvent event;
|
||||
};
|
||||
uint16_t mDataLength = 0;
|
||||
std::vector<uint8_t> mDataOutputBuffer;
|
||||
|
||||
bool mFilterEventReceived;
|
||||
std::map<uint32_t, string> mFilterIdToGoldenOutput;
|
||||
string mFilterIdToGoldenOutput;
|
||||
|
||||
std::map<uint32_t, std::unique_ptr<FilterMQ>> mFilterIdToMQ;
|
||||
std::unique_ptr<FilterMQ> mInputMQ;
|
||||
std::map<uint32_t, EventFlag*> mFilterIdToMQEventFlag;
|
||||
std::map<uint32_t, DemuxFilterEvent> mFilterIdToEvent;
|
||||
EventFlag* mInputMQEventFlag;
|
||||
uint32_t mFilterId;
|
||||
FilterEventType mFilterEventType;
|
||||
std::unique_ptr<FilterMQ> mFilterIdToMQ;
|
||||
EventFlag* mFilterIdToMQEventFlag;
|
||||
DemuxFilterEvent mFilterIdToEvent;
|
||||
|
||||
android::Mutex mMsgLock;
|
||||
android::Mutex mFilterOutputLock;
|
||||
android::Mutex mInputThreadLock;
|
||||
android::Condition mMsgCondition;
|
||||
android::Condition mFilterOutputCondition;
|
||||
|
||||
bool mKeepWritingInputFMQ = true;
|
||||
bool mInputThreadRunning;
|
||||
pthread_t mInputThread;
|
||||
pthread_t mFilterThread;
|
||||
|
||||
int mPidFilterOutputCount = 0;
|
||||
};
|
||||
|
||||
void DemuxCallback::startPlaybackInputThread(InputConf inputConf, MQDesc& inputMQDescriptor) {
|
||||
mInputMQ = std::make_unique<FilterMQ>(inputMQDescriptor, true /* resetPointers */);
|
||||
EXPECT_TRUE(mInputMQ);
|
||||
struct InputThreadArgs* threadArgs =
|
||||
(struct InputThreadArgs*)malloc(sizeof(struct InputThreadArgs));
|
||||
threadArgs->user = this;
|
||||
threadArgs->inputConf = &inputConf;
|
||||
threadArgs->keepWritingInputFMQ = &mKeepWritingInputFMQ;
|
||||
|
||||
pthread_create(&mInputThread, NULL, __threadLoopInput, (void*)threadArgs);
|
||||
pthread_setname_np(mInputThread, "test_playback_input_loop");
|
||||
}
|
||||
|
||||
void DemuxCallback::startFilterEventThread(DemuxFilterEvent event) {
|
||||
void FilterCallback::startFilterEventThread(DemuxFilterEvent event) {
|
||||
struct FilterThreadArgs* threadArgs =
|
||||
(struct FilterThreadArgs*)malloc(sizeof(struct FilterThreadArgs));
|
||||
threadArgs->user = this;
|
||||
@@ -326,7 +294,7 @@ void DemuxCallback::startFilterEventThread(DemuxFilterEvent event) {
|
||||
pthread_setname_np(mFilterThread, "test_playback_input_loop");
|
||||
}
|
||||
|
||||
void DemuxCallback::testFilterDataOutput() {
|
||||
void FilterCallback::testFilterDataOutput() {
|
||||
android::Mutex::Autolock autoLock(mMsgLock);
|
||||
while (mPidFilterOutputCount < 1) {
|
||||
if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
|
||||
@@ -338,95 +306,25 @@ void DemuxCallback::testFilterDataOutput() {
|
||||
ALOGW("[vts] pass and stop");
|
||||
}
|
||||
|
||||
void DemuxCallback::stopInputThread() {
|
||||
mInputThreadRunning = false;
|
||||
mKeepWritingInputFMQ = false;
|
||||
|
||||
android::Mutex::Autolock autoLock(mInputThreadLock);
|
||||
void FilterCallback::updateFilterMQ(MQDesc& filterMQDescriptor) {
|
||||
mFilterIdToMQ = std::make_unique<FilterMQ>(filterMQDescriptor, true /* resetPointers */);
|
||||
EXPECT_TRUE(mFilterIdToMQ);
|
||||
EXPECT_TRUE(EventFlag::createEventFlag(mFilterIdToMQ->getEventFlagWord(),
|
||||
&mFilterIdToMQEventFlag) == android::OK);
|
||||
}
|
||||
|
||||
void DemuxCallback::updateFilterMQ(uint32_t filterId, MQDesc& filterMQDescriptor) {
|
||||
mFilterIdToMQ[filterId] =
|
||||
std::make_unique<FilterMQ>(filterMQDescriptor, true /* resetPointers */);
|
||||
EXPECT_TRUE(mFilterIdToMQ[filterId]);
|
||||
EXPECT_TRUE(EventFlag::createEventFlag(mFilterIdToMQ[filterId]->getEventFlagWord(),
|
||||
&mFilterIdToMQEventFlag[filterId]) == android::OK);
|
||||
void FilterCallback::updateGoldenOutputMap(string goldenOutputFile) {
|
||||
mFilterIdToGoldenOutput = goldenOutputFile;
|
||||
}
|
||||
|
||||
void DemuxCallback::updateGoldenOutputMap(uint32_t filterId, string goldenOutputFile) {
|
||||
mFilterIdToGoldenOutput[filterId] = goldenOutputFile;
|
||||
}
|
||||
|
||||
void* DemuxCallback::__threadLoopInput(void* threadArgs) {
|
||||
DemuxCallback* const self =
|
||||
static_cast<DemuxCallback*>(((struct InputThreadArgs*)threadArgs)->user);
|
||||
self->inputThreadLoop(((struct InputThreadArgs*)threadArgs)->inputConf,
|
||||
((struct InputThreadArgs*)threadArgs)->keepWritingInputFMQ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DemuxCallback::inputThreadLoop(InputConf* inputConf, bool* keepWritingInputFMQ) {
|
||||
android::Mutex::Autolock autoLock(mInputThreadLock);
|
||||
mInputThreadRunning = true;
|
||||
|
||||
// Create the EventFlag that is used to signal the HAL impl that data have been
|
||||
// written into the Input FMQ
|
||||
EventFlag* inputMQEventFlag;
|
||||
EXPECT_TRUE(EventFlag::createEventFlag(mInputMQ->getEventFlagWord(), &inputMQEventFlag) ==
|
||||
android::OK);
|
||||
|
||||
// open the stream and get its length
|
||||
std::ifstream inputData(inputConf->inputDataFile, std::ifstream::binary);
|
||||
int writeSize = inputConf->setting.packetSize * 6;
|
||||
char* buffer = new char[writeSize];
|
||||
ALOGW("[vts] input thread loop start %s", inputConf->inputDataFile.c_str());
|
||||
if (!inputData.is_open()) {
|
||||
mInputThreadRunning = false;
|
||||
ALOGW("[vts] Error %s", strerror(errno));
|
||||
}
|
||||
|
||||
while (mInputThreadRunning) {
|
||||
// move the stream pointer for packet size * 6 every read until the end
|
||||
while (*keepWritingInputFMQ) {
|
||||
inputData.read(buffer, writeSize);
|
||||
if (!inputData) {
|
||||
int leftSize = inputData.gcount();
|
||||
if (leftSize == 0) {
|
||||
mInputThreadRunning = false;
|
||||
break;
|
||||
}
|
||||
inputData.clear();
|
||||
inputData.read(buffer, leftSize);
|
||||
// Write the left over of the input data and quit the thread
|
||||
if (leftSize > 0) {
|
||||
EXPECT_TRUE(mInputMQ->write((unsigned char*)&buffer[0], leftSize));
|
||||
inputMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
|
||||
}
|
||||
mInputThreadRunning = false;
|
||||
break;
|
||||
}
|
||||
// Write input FMQ and notify the Tuner Implementation
|
||||
EXPECT_TRUE(mInputMQ->write((unsigned char*)&buffer[0], writeSize));
|
||||
inputMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
|
||||
inputData.seekg(writeSize, inputData.cur);
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
ALOGW("[vts] Input thread end.");
|
||||
|
||||
delete[] buffer;
|
||||
inputData.close();
|
||||
}
|
||||
|
||||
void* DemuxCallback::__threadLoopFilter(void* threadArgs) {
|
||||
DemuxCallback* const self =
|
||||
static_cast<DemuxCallback*>(((struct FilterThreadArgs*)threadArgs)->user);
|
||||
void* FilterCallback::__threadLoopFilter(void* threadArgs) {
|
||||
FilterCallback* const self =
|
||||
static_cast<FilterCallback*>(((struct FilterThreadArgs*)threadArgs)->user);
|
||||
self->filterThreadLoop(((struct FilterThreadArgs*)threadArgs)->event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DemuxCallback::filterThreadLoop(DemuxFilterEvent& /* event */) {
|
||||
void FilterCallback::filterThreadLoop(DemuxFilterEvent& /* event */) {
|
||||
android::Mutex::Autolock autoLock(mFilterOutputLock);
|
||||
// Read from mFilterIdToMQ[event.filterId] per event and filter type
|
||||
|
||||
@@ -439,30 +337,184 @@ void DemuxCallback::filterThreadLoop(DemuxFilterEvent& /* event */) {
|
||||
// end thread
|
||||
}
|
||||
|
||||
bool DemuxCallback::readFilterEventData(uint32_t filterId) {
|
||||
bool FilterCallback::readFilterEventData() {
|
||||
bool result = false;
|
||||
DemuxFilterEvent filterEvent = mFilterIdToEvent[filterId];
|
||||
ALOGW("[vts] reading from filter FMQ %d", filterId);
|
||||
DemuxFilterEvent filterEvent = mFilterIdToEvent;
|
||||
ALOGW("[vts] reading from filter FMQ %d", mFilterId);
|
||||
// todo separate filter handlers
|
||||
for (int i = 0; i < filterEvent.events.size(); i++) {
|
||||
DemuxFilterPesEvent event = filterEvent.events[i].pes();
|
||||
mDataLength = event.dataLength;
|
||||
switch (mFilterEventType) {
|
||||
case FilterEventType::SECTION:
|
||||
mDataLength = filterEvent.events[i].section().dataLength;
|
||||
break;
|
||||
case FilterEventType::PES:
|
||||
mDataLength = filterEvent.events[i].pes().dataLength;
|
||||
break;
|
||||
case FilterEventType::MEDIA:
|
||||
break;
|
||||
case FilterEventType::RECORD:
|
||||
break;
|
||||
case FilterEventType::MMTPRECORD:
|
||||
break;
|
||||
case FilterEventType::DOWNLOAD:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// EXPECT_TRUE(mDataLength == goldenDataOutputBuffer.size()) << "buffer size does not
|
||||
// match";
|
||||
|
||||
mDataOutputBuffer.resize(mDataLength);
|
||||
result = mFilterIdToMQ[filterId]->read(mDataOutputBuffer.data(), mDataLength);
|
||||
result = mFilterIdToMQ->read(mDataOutputBuffer.data(), mDataLength);
|
||||
EXPECT_TRUE(result) << "can't read from Filter MQ";
|
||||
|
||||
/*for (int i = 0; i < mDataLength; i++) {
|
||||
EXPECT_TRUE(goldenDataOutputBuffer[i] == mDataOutputBuffer[i]) << "data does not match";
|
||||
}*/
|
||||
}
|
||||
mFilterIdToMQEventFlag[filterId]->wake(
|
||||
static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
|
||||
mFilterIdToMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
|
||||
return result;
|
||||
}
|
||||
|
||||
class DvrCallback : public IDvrCallback {
|
||||
public:
|
||||
virtual Return<void> onRecordStatus(RecordStatus /*status*/) override { return Void(); }
|
||||
|
||||
virtual Return<void> onPlaybackStatus(PlaybackStatus status) override {
|
||||
// android::Mutex::Autolock autoLock(mMsgLock);
|
||||
ALOGW("[vts] playback status %d", status);
|
||||
switch (status) {
|
||||
case PlaybackStatus::SPACE_EMPTY:
|
||||
case PlaybackStatus::SPACE_ALMOST_EMPTY:
|
||||
ALOGW("[vts] keep playback inputing %d", status);
|
||||
mKeepWritingPlaybackFMQ = true;
|
||||
break;
|
||||
case PlaybackStatus::SPACE_ALMOST_FULL:
|
||||
case PlaybackStatus::SPACE_FULL:
|
||||
ALOGW("[vts] stop playback inputing %d", status);
|
||||
mKeepWritingPlaybackFMQ = false;
|
||||
break;
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
void testFilterDataOutput();
|
||||
void stopPlaybackThread();
|
||||
|
||||
void startPlaybackInputThread(PlaybackConf playbackConf, MQDesc& playbackMQDescriptor);
|
||||
static void* __threadLoopPlayback(void* threadArgs);
|
||||
void playbackThreadLoop(PlaybackConf* playbackConf, bool* keepWritingPlaybackFMQ);
|
||||
|
||||
private:
|
||||
struct PlaybackThreadArgs {
|
||||
DvrCallback* user;
|
||||
PlaybackConf* playbackConf;
|
||||
bool* keepWritingPlaybackFMQ;
|
||||
};
|
||||
uint16_t mDataLength = 0;
|
||||
std::vector<uint8_t> mDataOutputBuffer;
|
||||
|
||||
std::map<uint32_t, std::unique_ptr<FilterMQ>> mFilterIdToMQ;
|
||||
std::unique_ptr<FilterMQ> mPlaybackMQ;
|
||||
std::map<uint32_t, EventFlag*> mFilterIdToMQEventFlag;
|
||||
std::map<uint32_t, DemuxFilterEvent> mFilterIdToEvent;
|
||||
EventFlag* mPlaybackMQEventFlag;
|
||||
|
||||
android::Mutex mMsgLock;
|
||||
android::Mutex mPlaybackThreadLock;
|
||||
android::Condition mMsgCondition;
|
||||
|
||||
bool mKeepWritingPlaybackFMQ = true;
|
||||
bool mPlaybackThreadRunning;
|
||||
pthread_t mPlaybackThread;
|
||||
|
||||
int mPidFilterOutputCount = 0;
|
||||
};
|
||||
|
||||
void DvrCallback::startPlaybackInputThread(PlaybackConf playbackConf,
|
||||
MQDesc& playbackMQDescriptor) {
|
||||
mPlaybackMQ = std::make_unique<FilterMQ>(playbackMQDescriptor, true /* resetPointers */);
|
||||
EXPECT_TRUE(mPlaybackMQ);
|
||||
struct PlaybackThreadArgs* threadArgs =
|
||||
(struct PlaybackThreadArgs*)malloc(sizeof(struct PlaybackThreadArgs));
|
||||
threadArgs->user = this;
|
||||
threadArgs->playbackConf = &playbackConf;
|
||||
threadArgs->keepWritingPlaybackFMQ = &mKeepWritingPlaybackFMQ;
|
||||
|
||||
pthread_create(&mPlaybackThread, NULL, __threadLoopPlayback, (void*)threadArgs);
|
||||
pthread_setname_np(mPlaybackThread, "test_playback_input_loop");
|
||||
}
|
||||
|
||||
void DvrCallback::stopPlaybackThread() {
|
||||
mPlaybackThreadRunning = false;
|
||||
mKeepWritingPlaybackFMQ = false;
|
||||
|
||||
android::Mutex::Autolock autoLock(mPlaybackThreadLock);
|
||||
}
|
||||
|
||||
void* DvrCallback::__threadLoopPlayback(void* threadArgs) {
|
||||
DvrCallback* const self =
|
||||
static_cast<DvrCallback*>(((struct PlaybackThreadArgs*)threadArgs)->user);
|
||||
self->playbackThreadLoop(((struct PlaybackThreadArgs*)threadArgs)->playbackConf,
|
||||
((struct PlaybackThreadArgs*)threadArgs)->keepWritingPlaybackFMQ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DvrCallback::playbackThreadLoop(PlaybackConf* playbackConf, bool* keepWritingPlaybackFMQ) {
|
||||
android::Mutex::Autolock autoLock(mPlaybackThreadLock);
|
||||
mPlaybackThreadRunning = true;
|
||||
|
||||
// Create the EventFlag that is used to signal the HAL impl that data have been
|
||||
// written into the Playback FMQ
|
||||
EventFlag* playbackMQEventFlag;
|
||||
EXPECT_TRUE(EventFlag::createEventFlag(mPlaybackMQ->getEventFlagWord(), &playbackMQEventFlag) ==
|
||||
android::OK);
|
||||
|
||||
// open the stream and get its length
|
||||
std::ifstream inputData(playbackConf->inputDataFile, std::ifstream::binary);
|
||||
int writeSize = playbackConf->setting.packetSize * 6;
|
||||
char* buffer = new char[writeSize];
|
||||
ALOGW("[vts] playback thread loop start %s", playbackConf->inputDataFile.c_str());
|
||||
if (!inputData.is_open()) {
|
||||
mPlaybackThreadRunning = false;
|
||||
ALOGW("[vts] Error %s", strerror(errno));
|
||||
}
|
||||
|
||||
while (mPlaybackThreadRunning) {
|
||||
// move the stream pointer for packet size * 6 every read until the end
|
||||
while (*keepWritingPlaybackFMQ) {
|
||||
inputData.read(buffer, writeSize);
|
||||
if (!inputData) {
|
||||
int leftSize = inputData.gcount();
|
||||
if (leftSize == 0) {
|
||||
mPlaybackThreadRunning = false;
|
||||
break;
|
||||
}
|
||||
inputData.clear();
|
||||
inputData.read(buffer, leftSize);
|
||||
// Write the left over of the input data and quit the thread
|
||||
if (leftSize > 0) {
|
||||
EXPECT_TRUE(mPlaybackMQ->write((unsigned char*)&buffer[0], leftSize));
|
||||
playbackMQEventFlag->wake(
|
||||
static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
|
||||
}
|
||||
mPlaybackThreadRunning = false;
|
||||
break;
|
||||
}
|
||||
// Write input FMQ and notify the Tuner Implementation
|
||||
EXPECT_TRUE(mPlaybackMQ->write((unsigned char*)&buffer[0], writeSize));
|
||||
playbackMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
|
||||
inputData.seekg(writeSize, inputData.cur);
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
ALOGW("[vts] Playback thread end.");
|
||||
|
||||
delete[] buffer;
|
||||
inputData.close();
|
||||
}
|
||||
|
||||
// Test environment for Tuner HIDL HAL.
|
||||
class TunerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
|
||||
public:
|
||||
@@ -494,16 +546,21 @@ class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
|
||||
sp<FrontendCallback> mFrontendCallback;
|
||||
sp<IDescrambler> mDescrambler;
|
||||
sp<IDemux> mDemux;
|
||||
sp<DemuxCallback> mDemuxCallback;
|
||||
sp<IDvr> mDvr;
|
||||
sp<IFilter> mFilter;
|
||||
std::map<uint32_t, sp<IFilter>> mFilters;
|
||||
std::map<uint32_t, sp<FilterCallback>> mFilterCallbacks;
|
||||
sp<FilterCallback> mFilterCallback;
|
||||
sp<DvrCallback> mDvrCallback;
|
||||
MQDesc mFilterMQDescriptor;
|
||||
MQDesc mInputMQDescriptor;
|
||||
MQDesc mPlaybackMQDescriptor;
|
||||
vector<uint32_t> mUsedFilterIds;
|
||||
|
||||
uint32_t mDemuxId;
|
||||
uint32_t mFilterId;
|
||||
|
||||
pthread_t mInputThread;
|
||||
bool mInputThreadRunning;
|
||||
pthread_t mPlaybackshread;
|
||||
bool mPlaybackThreadRunning;
|
||||
|
||||
::testing::AssertionResult createFrontend(int32_t frontendId);
|
||||
::testing::AssertionResult tuneFrontend(int32_t frontendId);
|
||||
@@ -512,16 +569,16 @@ class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
|
||||
::testing::AssertionResult createDemux();
|
||||
::testing::AssertionResult createDemuxWithFrontend(int32_t frontendId,
|
||||
FrontendSettings settings);
|
||||
::testing::AssertionResult getInputMQDescriptor();
|
||||
::testing::AssertionResult addInputToDemux(DemuxInputSettings setting);
|
||||
::testing::AssertionResult getPlaybackMQDescriptor();
|
||||
::testing::AssertionResult addPlaybackToDemux(PlaybackSettings setting);
|
||||
::testing::AssertionResult addFilterToDemux(DemuxFilterType type, DemuxFilterSettings setting);
|
||||
::testing::AssertionResult getFilterMQDescriptor(const uint32_t filterId);
|
||||
::testing::AssertionResult getFilterMQDescriptor();
|
||||
::testing::AssertionResult closeDemux();
|
||||
::testing::AssertionResult createDescrambler();
|
||||
::testing::AssertionResult closeDescrambler();
|
||||
|
||||
::testing::AssertionResult playbackDataFlowTest(vector<FilterConf> filterConf,
|
||||
InputConf inputConf,
|
||||
PlaybackConf playbackConf,
|
||||
vector<string> goldenOutputFiles);
|
||||
::testing::AssertionResult broadcastDataFlowTest(vector<FilterConf> filterConf,
|
||||
vector<string> goldenOutputFiles);
|
||||
@@ -665,39 +722,43 @@ class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
|
||||
return ::testing::AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
|
||||
::testing::AssertionResult TunerHidlTest::addInputToDemux(DemuxInputSettings setting) {
|
||||
::testing::AssertionResult TunerHidlTest::addPlaybackToDemux(PlaybackSettings setting) {
|
||||
Result status;
|
||||
|
||||
if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
|
||||
// Create demux callback
|
||||
if (!mDemuxCallback) {
|
||||
mDemuxCallback = new DemuxCallback();
|
||||
}
|
||||
// Create dvr callback
|
||||
mDvrCallback = new DvrCallback();
|
||||
|
||||
// Add playback input to the local demux
|
||||
status = mDemux->addInput(FMQ_SIZE_1M, mDemuxCallback);
|
||||
mDemux->openDvr(DvrType::PLAYBACK, FMQ_SIZE_1M, mDvrCallback,
|
||||
[&](Result result, const sp<IDvr>& dvr) {
|
||||
mDvr = dvr;
|
||||
status = result;
|
||||
});
|
||||
|
||||
if (status != Result::SUCCESS) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
|
||||
status = mDemux->configureInput(setting);
|
||||
DvrSettings dvrSetting;
|
||||
dvrSetting.playback(setting);
|
||||
status = mDvr->configure(dvrSetting);
|
||||
|
||||
return ::testing::AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
|
||||
::testing::AssertionResult TunerHidlTest::getInputMQDescriptor() {
|
||||
::testing::AssertionResult TunerHidlTest::getPlaybackMQDescriptor() {
|
||||
Result status;
|
||||
|
||||
if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
|
||||
if ((!mDemux && createDemux() == ::testing::AssertionFailure()) || !mDvr) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
|
||||
mDemux->getInputQueueDesc([&](Result result, const MQDesc& inputMQDesc) {
|
||||
mInputMQDescriptor = inputMQDesc;
|
||||
mDvr->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
|
||||
mPlaybackMQDescriptor = dvrMQDesc;
|
||||
status = result;
|
||||
});
|
||||
|
||||
@@ -713,13 +774,20 @@ class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
|
||||
}
|
||||
|
||||
// Create demux callback
|
||||
if (!mDemuxCallback) {
|
||||
mDemuxCallback = new DemuxCallback();
|
||||
}
|
||||
mFilterCallback = new FilterCallback();
|
||||
|
||||
// Add filter to the local demux
|
||||
mDemux->addFilter(type, FMQ_SIZE_4K, mDemuxCallback, [&](Result result, uint32_t filterId) {
|
||||
// TODO use a map to save all the filter id and FMQ
|
||||
mDemux->openFilter(type, FMQ_SIZE_16M, mFilterCallback,
|
||||
[&](Result result, const sp<IFilter>& filter) {
|
||||
mFilter = filter;
|
||||
status = result;
|
||||
});
|
||||
|
||||
if (status != Result::SUCCESS) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
|
||||
mFilter->getId([&](Result result, uint32_t filterId) {
|
||||
mFilterId = filterId;
|
||||
status = result;
|
||||
});
|
||||
@@ -728,20 +796,64 @@ class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
|
||||
mFilterCallback->setFilterId(mFilterId);
|
||||
|
||||
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;
|
||||
}
|
||||
break;
|
||||
case DemuxFilterMainType::MMTP:
|
||||
/*mmtpSettings*/
|
||||
break;
|
||||
case DemuxFilterMainType::IP:
|
||||
/*ipSettings*/
|
||||
break;
|
||||
case DemuxFilterMainType::TLV:
|
||||
/*tlvSettings*/
|
||||
break;
|
||||
case DemuxFilterMainType::ALP:
|
||||
/*alpSettings*/
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mFilterCallback->setFilterEventType(eventType);
|
||||
|
||||
// Configure the filter
|
||||
status = mDemux->configureFilter(mFilterId, setting);
|
||||
status = mFilter->configure(setting);
|
||||
|
||||
return ::testing::AssertionResult(status == Result::SUCCESS);
|
||||
}
|
||||
|
||||
::testing::AssertionResult TunerHidlTest::getFilterMQDescriptor(const uint32_t filterId) {
|
||||
::testing::AssertionResult TunerHidlTest::getFilterMQDescriptor() {
|
||||
Result status;
|
||||
|
||||
if (!mDemux) {
|
||||
if (!mDemux || !mFilter) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
|
||||
mDemux->getFilterQueueDesc(filterId, [&](Result result, const MQDesc& filterMQDesc) {
|
||||
mFilter->getQueueDesc([&](Result result, const MQDesc& filterMQDesc) {
|
||||
mFilterMQDescriptor = filterMQDesc;
|
||||
status = result;
|
||||
});
|
||||
@@ -750,7 +862,8 @@ class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
|
||||
}
|
||||
|
||||
::testing::AssertionResult TunerHidlTest::playbackDataFlowTest(
|
||||
vector<FilterConf> filterConf, InputConf inputConf, vector<string> /*goldenOutputFiles*/) {
|
||||
vector<FilterConf> filterConf, PlaybackConf playbackConf,
|
||||
vector<string> /*goldenOutputFiles*/) {
|
||||
Result status;
|
||||
int filterIdsSize;
|
||||
// Filter Configuration Module
|
||||
@@ -758,45 +871,58 @@ class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
|
||||
if (addFilterToDemux(filterConf[i].type, filterConf[i].setting) ==
|
||||
::testing::AssertionFailure() ||
|
||||
// TODO use a map to save the FMQs/EvenFlags and pass to callback
|
||||
getFilterMQDescriptor(mFilterId) == ::testing::AssertionFailure()) {
|
||||
getFilterMQDescriptor() == ::testing::AssertionFailure()) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
filterIdsSize = mUsedFilterIds.size();
|
||||
mUsedFilterIds.resize(filterIdsSize + 1);
|
||||
mUsedFilterIds[filterIdsSize] = mFilterId;
|
||||
mDemuxCallback->updateFilterMQ(mFilterId, mFilterMQDescriptor);
|
||||
// mDemuxCallback->updateGoldenOutputMap(mFilterId, goldenOutputFiles[i]);
|
||||
status = mDemux->startFilter(mFilterId);
|
||||
mFilters[mFilterId] = mFilter;
|
||||
mFilterCallbacks[mFilterId] = mFilterCallback;
|
||||
mFilterCallback->updateFilterMQ(mFilterMQDescriptor);
|
||||
// mDemuxCallback->updateGoldenOutputMap(goldenOutputFiles[i]);
|
||||
status = mFilter->start();
|
||||
if (status != Result::SUCCESS) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
}
|
||||
|
||||
// Playback Input Module
|
||||
DemuxInputSettings inputSetting = inputConf.setting;
|
||||
if (addInputToDemux(inputSetting) == ::testing::AssertionFailure() ||
|
||||
getInputMQDescriptor() == ::testing::AssertionFailure()) {
|
||||
PlaybackSettings playbackSetting = playbackConf.setting;
|
||||
if (addPlaybackToDemux(playbackSetting) == ::testing::AssertionFailure() ||
|
||||
getPlaybackMQDescriptor() == ::testing::AssertionFailure()) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
mDemuxCallback->startPlaybackInputThread(inputConf, mInputMQDescriptor);
|
||||
status = mDemux->startInput();
|
||||
for (int i = 0; i <= filterIdsSize; i++) {
|
||||
if (mDvr->attachFilter(mFilters[mUsedFilterIds[i]]) != Result::SUCCESS) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
}
|
||||
mDvrCallback->startPlaybackInputThread(playbackConf, mPlaybackMQDescriptor);
|
||||
status = mDvr->start();
|
||||
if (status != Result::SUCCESS) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
|
||||
// Data Verify Module
|
||||
mDemuxCallback->testFilterDataOutput();
|
||||
mDemuxCallback->stopInputThread();
|
||||
std::map<uint32_t, sp<FilterCallback>>::iterator it;
|
||||
for (it = mFilterCallbacks.begin(); it != mFilterCallbacks.end(); it++) {
|
||||
it->second->testFilterDataOutput();
|
||||
}
|
||||
mDvrCallback->stopPlaybackThread();
|
||||
|
||||
// Clean Up Module
|
||||
for (int i = 0; i <= filterIdsSize; i++) {
|
||||
if (mDemux->stopFilter(mUsedFilterIds[i]) != Result::SUCCESS) {
|
||||
if (mFilters[mUsedFilterIds[i]]->stop() != Result::SUCCESS) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
}
|
||||
if (mDemux->stopInput() != Result::SUCCESS) {
|
||||
if (mDvr->stop() != Result::SUCCESS) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
mUsedFilterIds.clear();
|
||||
mFilterCallbacks.clear();
|
||||
mFilters.clear();
|
||||
return closeDemux();
|
||||
}
|
||||
|
||||
@@ -831,31 +957,39 @@ class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
|
||||
if (addFilterToDemux(filterConf[i].type, filterConf[i].setting) ==
|
||||
::testing::AssertionFailure() ||
|
||||
// TODO use a map to save the FMQs/EvenFlags and pass to callback
|
||||
getFilterMQDescriptor(mFilterId) == ::testing::AssertionFailure()) {
|
||||
getFilterMQDescriptor() == ::testing::AssertionFailure()) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
filterIdsSize = mUsedFilterIds.size();
|
||||
mUsedFilterIds.resize(filterIdsSize + 1);
|
||||
mUsedFilterIds[filterIdsSize] = mFilterId;
|
||||
mDemuxCallback->updateFilterMQ(mFilterId, mFilterMQDescriptor);
|
||||
status = mDemux->startFilter(mFilterId);
|
||||
mFilters[mFilterId] = mFilter;
|
||||
mFilterCallbacks[mFilterId] = mFilterCallback;
|
||||
mFilterCallback->updateFilterMQ(mFilterMQDescriptor);
|
||||
status = mFilter->start();
|
||||
if (status != Result::SUCCESS) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
}
|
||||
|
||||
// Data Verify Module
|
||||
mDemuxCallback->testFilterDataOutput();
|
||||
std::map<uint32_t, sp<FilterCallback>>::iterator it;
|
||||
for (it = mFilterCallbacks.begin(); it != mFilterCallbacks.end(); it++) {
|
||||
it->second->testFilterDataOutput();
|
||||
}
|
||||
|
||||
// Clean Up Module
|
||||
for (int i = 0; i <= filterIdsSize; i++) {
|
||||
if (mDemux->stopFilter(mUsedFilterIds[i]) != Result::SUCCESS) {
|
||||
if (mFilters[mUsedFilterIds[i]]->stop() != Result::SUCCESS) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
}
|
||||
if (mFrontend->stopTune() != Result::SUCCESS) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
mUsedFilterIds.clear();
|
||||
mFilterCallbacks.clear();
|
||||
mFilters.clear();
|
||||
return closeDemux();
|
||||
}
|
||||
|
||||
@@ -992,7 +1126,7 @@ TEST_F(TunerHidlTest, CloseDescrambler) {
|
||||
/*
|
||||
* DATA FLOW TESTS
|
||||
*/
|
||||
TEST_F(TunerHidlTest, PlaybackDataFlowWithPesFilterTest) {
|
||||
TEST_F(TunerHidlTest, PlaybackDataFlowWithSectionFilterTest) {
|
||||
description("Feed ts data from playback and configure pes filter to get output");
|
||||
|
||||
// todo modulize the filter conf parser
|
||||
@@ -1000,32 +1134,39 @@ TEST_F(TunerHidlTest, PlaybackDataFlowWithPesFilterTest) {
|
||||
filterConf.resize(1);
|
||||
|
||||
DemuxFilterSettings filterSetting;
|
||||
DemuxFilterPesDataSettings pesFilterSetting{
|
||||
DemuxTsFilterSettings tsFilterSetting{
|
||||
.tpid = 18,
|
||||
};
|
||||
filterSetting.pesData(pesFilterSetting);
|
||||
FilterConf pesFilterConf{
|
||||
.type = DemuxFilterType::PES,
|
||||
DemuxFilterSectionSettings sectionFilterSetting;
|
||||
tsFilterSetting.filterSettings.section(sectionFilterSetting);
|
||||
filterSetting.ts(tsFilterSetting);
|
||||
|
||||
DemuxFilterType type{
|
||||
.mainType = DemuxFilterMainType::TS,
|
||||
};
|
||||
type.subType.tsFilterType(DemuxTsFilterType::SECTION);
|
||||
FilterConf sectionFilterConf{
|
||||
.type = type,
|
||||
.setting = filterSetting,
|
||||
};
|
||||
filterConf[0] = pesFilterConf;
|
||||
filterConf[0] = sectionFilterConf;
|
||||
|
||||
DemuxInputSettings inputSetting{
|
||||
PlaybackSettings playbackSetting{
|
||||
.statusMask = 0xf,
|
||||
.lowThreshold = 0x1000,
|
||||
.highThreshold = 0x07fff,
|
||||
.dataFormat = DemuxDataFormat::TS,
|
||||
.dataFormat = DataFormat::TS,
|
||||
.packetSize = 188,
|
||||
};
|
||||
|
||||
InputConf inputConf{
|
||||
PlaybackConf playbackConf{
|
||||
.inputDataFile = "/vendor/etc/test1.ts",
|
||||
.setting = inputSetting,
|
||||
.setting = playbackSetting,
|
||||
};
|
||||
|
||||
vector<string> goldenOutputFiles;
|
||||
|
||||
ASSERT_TRUE(playbackDataFlowTest(filterConf, inputConf, goldenOutputFiles));
|
||||
ASSERT_TRUE(playbackDataFlowTest(filterConf, playbackConf, goldenOutputFiles));
|
||||
}
|
||||
|
||||
TEST_F(TunerHidlTest, BroadcastDataFlowWithPesFilterTest) {
|
||||
@@ -1036,12 +1177,19 @@ TEST_F(TunerHidlTest, BroadcastDataFlowWithPesFilterTest) {
|
||||
filterConf.resize(1);
|
||||
|
||||
DemuxFilterSettings filterSetting;
|
||||
DemuxFilterPesDataSettings pesFilterSetting{
|
||||
.tpid = 18,
|
||||
DemuxTsFilterSettings tsFilterSetting{
|
||||
.tpid = 119,
|
||||
};
|
||||
filterSetting.pesData(pesFilterSetting);
|
||||
DemuxFilterPesDataSettings pesFilterSetting;
|
||||
tsFilterSetting.filterSettings.pesData(pesFilterSetting);
|
||||
filterSetting.ts(tsFilterSetting);
|
||||
|
||||
DemuxFilterType type{
|
||||
.mainType = DemuxFilterMainType::TS,
|
||||
};
|
||||
type.subType.tsFilterType(DemuxTsFilterType::PES);
|
||||
FilterConf pesFilterConf{
|
||||
.type = DemuxFilterType::PES,
|
||||
.type = type,
|
||||
.setting = filterSetting,
|
||||
};
|
||||
filterConf[0] = pesFilterConf;
|
||||
|
||||
Reference in New Issue
Block a user