Android 11 이상에서는 Android 튜너 프레임워크를 사용하여 A/V 콘텐츠를 제공할 수 있습니다. 프레임워크는 공급업체의 하드웨어 파이프라인을 사용하므로 저사양 및 고사양 SoC에 모두 적합합니다. 프레임워크는 신뢰할 수 있는 실행 환경(TEE)과 보안 미디어 경로(SMP)로 보호되는 A/V 콘텐츠를 안전하게 제공하는 방법을 제공하므로 고도로 제한된 콘텐츠 보호 환경에서 사용할 수 있습니다.
튜너와 Android CAS 간에 표준화된 인터페이스로 인해 튜너 공급업체와 CAS 공급업체 간 통합이 더 빨라집니다. 튜너 인터페이스는 MediaCodec
및 AudioTrack
과 함께 작동하여 Android TV용 하나의 글로벌 솔루션을 빌드합니다.
튜너 인터페이스는 주요 방송 표준에 기반하여 디지털 TV와 아날로그 TV를 모두 지원합니다.
구성요소
Android 11의 경우 3가지 구성요소가 TV 플랫폼용으로 특별히 설계되었습니다.
- 튜너 HAL: 프레임워크와 공급업체 간 인터페이스
- Tuner SDK API: 프레임워크와 앱 간 인터페이스
- 튜너 리소스 관리자(TRM): 튜너 HW 리소스 조정
Android 11에서는 다음 구성요소가 개선되었습니다.
- CAS V2
TvInputService
또는 TV 입력 서비스(TIS)TvInputManagerService
또는 TV 입력 관리자 서비스(TIMS)MediaCodec
또는 미디어 코덱AudioTrack
또는 오디오 트랙MediaResourceManager
또는 미디어 리소스 관리자(MRM)
그림 1. Android TV 구성요소 간 상호작용
특징
프런트엔드는 아래의 DTV 표준을 지원합니다.
- ATSC
- ATSC3
- DVB C/S/T
- ISDB S/S3/T
- 아날로그
튜너 HAL 1.1 이상을 사용하는 Android 12의 프런트엔드는 아래 DTV 표준을 지원합니다.
- DTMB
Demux는 아래의 스트림 프로토콜을 지원합니다.
- 전송 스트림(TS)
- MPEG 미디어 전송 프로토콜(MMTP)
- 인터넷 프로토콜(IP)
- 유형 길이 값(TLV)
- ATSC 링크 레이어 프로토콜(ALP)
디스크램블러는 아래의 콘텐츠 보호 조치를 지원합니다.
- 보안 미디어 경로
- 명확한 미디어 경로
- 보안 로컬 녹화
- 보안 로컬 재생
튜너 API는 아래의 사용 사례를 지원합니다.
- 검사
- 실시간
- 재생
- 녹화
튜너, MediaCodec
, AudioTrack
은 아래의 데이터 흐름 모드를 지원합니다.
- 명확한 메모리 버퍼가 있는 ES 페이로드
- 보안 메모리 핸들이 있는 ES 페이로드
- 패스 스루
전반적인 디자인
튜너 HAL은 Android 프레임워크와 공급업체의 하드웨어 간에 정의됩니다.
- 프레임워크가 공급업체에 기대하는 내용과 공급업체가 이를 실행할 수 있는 방법을 설명합니다.
IFrontend
,IDemux
,IDescrambler
,IFilter
,IDvr
,ILnb
인터페이스를 통해 프런트엔드, demux, 디스크램블러의 기능을 프레임워크로 내보냅니다.- 튜너 HAL을
MediaCodec
과AudioTrack
등의 다른 프레임워크 구성요소와 통합하는 함수를 포함합니다.
튜너 자바 클래스와 네이티브 클래스가 만들어집니다.
- Tuner Java API를 사용하면 앱이 공개 API를 통해 튜너 HAL에 액세스할 수 있습니다.
- 네이티브 클래스를 사용하면 권한을 제어할 수 있고 튜너 HAL로 대량의 녹화나 재생 데이터를 처리할 수 있습니다.
- 네이티브 튜너 모듈은 튜너 자바 클래스와 튜너 HAL 사이의 브리지입니다.
TRM 클래스가 만들어집니다.
- 프런트엔드, LNB, CAS 세션, TV 입력 HAL의 TV 입력 장치와 같은 제한된 튜너 리소스를 관리합니다.
- 앱에서 부족한 리소스를 회수하는 규칙을 적용합니다. 기본 규칙은 포그라운드 우선입니다.
미디어 CAS와 CAS HAL은 아래 기능으로 개선되었습니다.
- 다양한 사용과 알고리즘의 CAS 세션을 엽니다.
- CICAM 삭제 및 삽입과 같은 동적 CAS 시스템을 지원합니다.
- 키 토큰을 제공하여 튜너 HAL과 통합합니다.
MediaCodec
과 AudioTrack
이 아래 기능으로 개선되었습니다.
- 보안 A/V 메모리를 콘텐츠 입력으로 사용합니다.
- 터널링된 재생에서 하드웨어 A/V 동기화를 실행하도록 구성되었습니다.
ES_payload
및 패스 스루 모드 지원을 구성했습니다.
그림 2. 튜너 HAL 내의 구성요소 다이어그램
전체 워크플로
아래 다이어그램은 실시간 방송 재생의 호출 시퀀스를 보여 줍니다.
설정
그림 3. 실시간 방송 재생의 설정 시퀀스
A/V 처리
그림 4. 실시간 방송 재생의 A/V 처리
스크램블링된 콘텐츠 처리
그림 5. 실시간 방송 재생의 스크램블링된 콘텐츠 처리
A/V 데이터 처리
그림 6. 실시간 방송 재생의 A/V 처리
Tuner SDK API
Tuner SDK API는 튜너 JNI, 튜너 HAL, TunerResourceManager
와의 상호작용을 처리합니다. TIS 앱은 Tuner SDK API를 사용하여 튜너 리소스와 하위 구성요소(필터, 디스크램블러 등)에 액세스합니다. 프런트엔드와 demux는 내부 구성요소입니다.
그림 7. Tuner SDK API와의 상호작용
버전
Android 12부터 Tuner SDK API는 튜너 1.0의 이전 버전과 호환되는 버전 업그레이드인 튜너 HAL 1.1의 새로운 기능을 지원합니다.
다음 API를 사용하여 실행 중인 HAL 버전을 확인합니다.
android.media.tv.tuner.TunerVersionChecker.getTunerVersion()
필요한 최소 HAL 버전은 새로운 Android 12 API 문서에서 확인할 수 있습니다.
패키지
Tuner SDK API는 아래와 같은 네 가지 패키지를 제공합니다.
android.media.tv.tuner
android.media.tv.tuner.frontend
android.media.tv.tuner.filter
android.media.tv.tuner.dvr
그림 8. Tuner SDK API 패키지
Android.media.tv.tuner
튜너 패키지는 튜너 프레임워크를 사용하기 위한 진입점입니다. TIS 앱은 패키지를 통해 초기 설정과 콜백을 지정하여 리소스 인스턴스를 초기화하고 가져옵니다.
tuner()
:useCase
및sessionId
매개변수를 지정하여 튜너 인스턴스를 초기화합니다.tune()
:FrontendSetting
매개변수를 지정하여 프런트엔드 리소스와 튠을 가져옵니다.openFilter()
: 필터 유형을 지정하여 필터 인스턴스를 가져옵니다.openDvrRecorder()
: 버퍼 크기를 지정하여 녹화 인스턴스를 가져옵니다.openDvrPlayback()
: 버퍼 크기를 지정하여 재생 인스턴스를 가져옵니다.openDescrambler()
: 디스크램블러 인스턴스를 가져옵니다.openLnb()
: 내부 LNB 인스턴스를 가져옵니다.openLnbByName()
: 외부 LNB 인스턴스를 가져옵니다.openTimeFilter()
: 시간 필터 인스턴스를 가져옵니다.
튜너 패키지는 필터, DVR, 프런트엔드 패키지에서 다루지 않는 기능을 제공합니다. 아래에 기능이 나열되어 있습니다.
cancelTuning
scan
/cancelScanning
getAvSyncHwId
getAvSyncTime
connectCiCam1
/disconnectCiCam
shareFrontendFromTuner
updateResourcePriority
setOnTuneEventListener
setResourceLostListener
Android.media.tv.tuner.frontend
프런트엔드 패키지에는 프런트엔드 관련 설정, 정보, 상태, 이벤트, 기능 모음이 포함되어 있습니다.
클래스
FrontendSettings
는 아래 클래스에 따라 다양한 DTV 표준에서 파생됩니다.
AnalogFrontendSettings
Atsc3FrontendSettings
AtscFrontendSettings
DvbcFrontendSettings
DvbsFrontendSettings
DvbtFrontendSettings
Isdbs3FrontendSettings
IsdbsFrontendSettings
IsdbtFrontendSettings
튜너 HAL 1.1 이상을 사용하는 Android 12부터 다음 DTV 표준이 지원됩니다.
DtmbFrontendSettings
FrontendCapabilities
는 아래 클래스에 따라 다양한 DTV 표준에서 파생됩니다.
AnalogFrontendCapabilities
Atsc3FrontendCapabilities
AtscFrontendCapabilities
DvbcFrontendCapabilities
DvbsFrontendCapabilities
DvbtFrontendCapabilities
Isdbs3FrontendCapabilities
IsdbsFrontendCapabilities
IsdbtFrontendCapabilities
튜너 HAL 1.1 이상을 사용하는 Android 12부터 다음 DTV 표준이 지원됩니다.
DtmbFrontendCapabilities
FrontendInfo
는 프런트엔드 정보를 검색합니다.
FrontendStatus
는 프런트엔드의 현재 상태를 검색합니다.
OnTuneEventListener
는 프런트엔드의 이벤트를 수신합니다.
TIS 앱은 ScanCallback
을 사용하여 프런트엔드의 검색 메시지를 처리합니다.
채널 검색
TV를 설정하기 위해 앱은 가능한 주파수를 검색하고 사용자가 액세스할 수 있는 채널 라인업을 만듭니다. TIS는 Tuner.tune
이나 Tuner.scan(BLIND_SCAN)
, Tuner.scan(AUTO_SCAN)
을 사용하여 채널 검색을 완료할 수도 있습니다.
TIS에 주파수, 표준(예: T/T2, S/S2), 필요한 추가 정보(예: PLD ID)와 같은 신호의 정확한 전송 정보가 있으면 더 빠른 옵션으로 Tuner.tune
을 사용하는 것이 좋습니다.
사용자가 Tuner.tune
을 호출할 때 다음 작업이 발생합니다.
- TIS는
Tuner.tune
을 사용하여FrontendSettings
를 필수 정보로 채웁니다. - HAL은 신호가 잠겨 있으면 튠
LOCKED
메시지를 보고합니다. - TIS는
Frontend.getStatus
를 사용하여 필요한 정보를 수집합니다. - TIS는 주파수 목록에서 사용 가능한 다음 주파수로 이동합니다.
TIS는 모든 주파수가 소진될 때까지 Tuner.tune
을 다시 호출합니다.
조정 중에 stopTune()
이나 close()
를 호출하여 Tuner.tune
호출을 일시중지하거나 종료할 수 있습니다.
Tuner.scan(AUTO_SCAN)
TIS에 Tuner.tune
을 사용하기에 충분한 정보는 없지만 주파수 목록과 표준 유형(예: DVB T/C/S)이 있다면 Tuner.scan(AUTO_SCAN)
을 사용하는 것이 좋습니다.
사용자가 Tuner.scan(AUTO_SCAN)
을 호출할 때 다음 작업이 발생합니다.
TIS는 주파수로 채워진
FrontendSettings
와 함께Tuner.scan(AUTO_SCAN)
을 사용합니다.HAL은 신호가 잠겨 있으면 검색
LOCKED
메시지를 보고합니다. HAL은 기타 검색 메시지도 보고하여 신호에 관한 추가 정보를 제공할 수도 있습니다.TIS는
Frontend.getStatus
를 사용하여 필요한 정보를 수집합니다.TIS는 HAL이 동일한 주파수의 다음 설정으로 계속 진행하도록
Tuner.scan
을 호출합니다.FrontendSettings
구조가 비어 있으면 HAL은 사용 가능한 다음 설정을 사용합니다. 그 외의 경우 HAL은 일회성 검색용으로FrontendSettings
를 사용하고END
를 전송하여 검색 작업이 완료되었다고 나타냅니다.TIS는 주파수의 모든 설정이 소진될 때까지 위의 작업을 반복합니다.
HAL은
END
를 전송하여 검색 작업이 완료되었다고 나타냅니다.TIS는 주파수 목록에서 사용 가능한 다음 주파수로 이동합니다.
TIS는 모든 주파수가 소진될 때까지 Tuner.scan(AUTO_SCAN)
을 다시 호출합니다.
검색 중에 stopScan()
이나 close()
를 호출하여 검색을 일시중지하거나 종료할 수 있습니다.
Tuner.scan(BLIND_SCAN)
TIS에 주파수 목록이 없고 공급업체 HAL이 사용자 지정 프런트엔드의 주파수를 검색하여 프런트엔드 리소스를 가져올 수 있다면 Tuner.scan(BLIND_SCAN)
을 사용하는 것이 좋습니다.
- TIS는
Tuner.scan(BLIND_SCAN)
을 사용합니다. 시작 주파수의FrontendSettings
에서 주파수를 지정할 수 있지만 TIS는FrontendSettings
의 다른 설정을 무시합니다. - HAL은 신호가 잠겨 있으면 검색
LOCKED
메시지를 보고합니다. - TIS는
Frontend.getStatus
를 사용하여 필요한 정보를 수집합니다. - TIS는
Tuner.scan
을 다시 호출하여 검색을 계속합니다.FrontendSettings
는 무시됩니다. - TIS는 주파수의 모든 설정이 소진될 때까지 위의 작업을 반복합니다. HAL은 TIS에서 필요한 작업 없이 주파수를 증분합니다.
HAL은
PROGRESS
를 보고합니다.
TIS는 모든 주파수가 소진될 때까지 Tuner.scan(AUTO_SCAN)
을 다시 호출합니다.
HAL은 END
를 보고하여 검색 작업이 완료되었다고 나타냅니다.
검색 중에 stopScan()
이나 close()
를 호출하여 검색을 일시중지하거나 종료할 수 있습니다.
그림 9. TIS 검색 흐름 다이어그램
Android.media.tv.tuner.filter
필터 패키지는 구성, 설정, 콜백, 이벤트와 함께 필터 작업의 모음입니다. 패키지에는 아래의 작업이 포함됩니다. 전체 작업 목록은 Android 소스 코드를 참고하세요.
configure()
start()
stop()
flush()
read()
전체 목록은 Android 소스 코드를 참고하세요.
FilterConfiguration
은 아래의 클래스에서 파생됩니다. 구성은 기본 필터 유형을 위한 것으로, 필터가 데이터를 추출하는 데 사용하는 프로토콜을 지정합니다.
AlpFilterConfiguration
IpFilterConfiguration
MmtpFilterConfiguration
TlvFilterConfiguration
TsFilterConfiguration
설정은 아래의 클래스에서 파생됩니다. 설정은 필터 하위유형을 위한 것으로, 필터가 제외할 수 있는 데이터의 종류를 지정합니다.
SectionSettings
AvSettings
PesSettings
RecordSettings
DownloadSettings
FilterEvent
는 아래의 클래스에서 파생되어 다양한 종류의 데이터 이벤트를 보고합니다.
SectionEvent
MediaEvent
PesEvent
TsRecordEvent
MmtpRecordEvent
TemiEvent
DownloadEvent
IpPayloadEvent
튜너 HAL 1.1 이상을 사용하는 Android 12부터 다음 이벤트가 지원됩니다.
IpCidChangeEvent
RestartEvent
ScramblingStatusEvent
필터의 이벤트 및 데이터 형식
필터 유형 | 플래그 | 이벤트 | 데이터 작업 | 데이터 형식 |
---|---|---|---|---|
TS.SECTION MMTP.SECTION IP.SECTION TLV.SECTION ALP.SECTION |
isRaw: |
필수:DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 권장: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER |
이벤트 및 내부 일정에 따라Filter.read(buffer, offset, adjustedSize) 를 1회 이상 실행합니다.데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다. |
구성된 세션 패키지 하나가 다른 세션 패키지로 FMQ에 채워집니다. |
isRaw: |
필수:DemuxFilterEvent::DemuxFilterSectionEvent[n] DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 선택사항: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER |
for i=0; i<n; i++ 데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다. |
||
TS.PES |
isRaw: |
필수:DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 권장: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER |
이벤트 및 내부 일정에 따라Filter.read(buffer, offset, adjustedSize) 를 1회 이상 실행합니다.데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다. |
구성된 PES 패키지 하나가 다른 PES 패키지로 FMQ에 채워집니다. |
isRaw: |
필수:DemuxFilterEvent::DemuxFilterPesEvent[n] DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 선택사항: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER |
for i=0; i<n; i++ 데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다. |
||
MMTP.PES |
isRaw: |
필수:DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 권장: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER |
이벤트 및 내부 일정에 따라Filter.read(buffer, offset, adjustedSize) 를 1회 이상 실행합니다.데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다. |
구성된 MFU 패키지 하나가 다른 MFU 패키지로 FMQ에 채워집니다. |
isRaw: |
필수:DemuxFilterEvent::DemuxFilterPesEvent[n] DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 선택사항: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER |
for i=0; i<n; i++ 데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다. |
||
TS.TS |
N/A | 필수:DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 권장: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER |
이벤트 및 내부 일정에 따라Filter.read(buffer, offset, adjustedSize) 를 1회 이상 실행합니다.데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다. |
ts 헤더가 있는 필터링된 ts 가FMQ에 채워집니다. |
TS.Audio TS.Video MMTP.Audio MMTP.Video |
isPassthrough: |
선택사항:DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW |
클라이언트는 DemuxFilterStatus::DATA_READY 를 수신한 후 MediaCodec 을 시작할 수 있습니다.클라이언트는 DemuxFilterStatus::DATA_OVERFLOW 를 수신한 후 Filter.flush 를 호출할 수 있습니다. |
N/A |
isPassthrough: |
필수:DemuxFilterEvent::DemuxFilterMediaEvent[n] DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 선택사항: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER |
MediaCodec 사용 방법:for i=0; i<n; i++ AudioTrack 의 직접 오디오 사용 방법:for i=0; i<n; i++ |
ION 메모리의 ES 또는 부분 ES 데이터 | |
TS.PCR IP.NTP ALP.PTP |
N/A | 필수: N/A 선택사항: N/A |
N/A | 해당 사항 없음 |
TS.RECORD |
N/A | 필수: DemuxFilterEvent::DemuxFilterTsRecordEvent[n] RecordStatus::DATA_READY RecordStatus::DATA_OVERFLOW RecordStatus::LOW_WATER RecordStatus::HIGH_WATER 선택사항: DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER |
색인 데이터의 경우:for i=0; i<n; i++ 녹화된 콘텐츠의 경우 RecordStatus::* 와 내부 일정에 따라 다음 중 하나를 실행합니다.
|
색인 데이터의 경우: 이벤트 페이로드에서 가져옵니다. 녹화된 콘텐츠의 경우: FMQ에 다중 TS 스트림이 채워져 있습니다. |
TS.TEMI |
N/A | 필수:DemuxFilterEvent::DemuxFilterTemiEvent[n] 선택사항: DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER |
for i=0; i<n; i++ |
N/A |
MMTP.MMTP |
N/A | 필수:DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 권장: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER |
이벤트 및 내부 일정에 따라Filter.read(buffer, offset, adjustedSize) 를 1회 이상 실행합니다.데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다. |
mmtp 헤더가 있는 필터링된 mmtp 가FMQ에 채워집니다. |
MMTP.RECORD |
N/A | 필수:DemuxFilterEvent::DemuxFilterMmtpRecordEvent[n] RecordStatus::DATA_READY RecordStatus::DATA_OVERFLOW RecordStatus::LOW_WATER RecordStatus::HIGH_WATER 선택사항: DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER |
색인 데이터의 경우: for i=0; i<n; i++ 녹화된 콘텐츠의 경우 RecordStatus::* 및 내부 일정에 따라 다음 중 하나를 실행합니다.
|
색인 데이터의 경우: 이벤트 페이로드에서 가져옵니다. 녹화된 콘텐츠의 경우: 녹화된 다중 스트림이 FMQ에 채워져 있습니다. 녹화용 필터 소스가 TLV.TLV 에서 패스 스루가 있는 IP.IP 이면 녹화된 스트림에는 TLV 및 IP 헤더가 있습니다. |
MMTP.DOWNLOAD |
N/A | 필수:DemuxFilterEvent::DemuxFilterDownloadEvent[n] DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 선택사항: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER |
for i=0; i<n; i++
Filter.read(buffer, offset, DemuxFilterDownloadEvent[i].size) 데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다. |
다운로드 패키지는 다른 IP 다운로드 패키지로 FMQ에 채워집니다. |
IP.IP_PAYLOAD |
N/A | 필수:DemuxFilterEvent::DemuxFilterIpPayloadEvent[n] DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 선택사항: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER |
for i=0; i<n; i++
Filter.read(buffer, offset, DemuxFilterIpPayloadEvent[i].size) 데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다. |
IP 페이로드 패키지는 다른 IP 페이로드 패키지로 FMQ에 채워집니다. |
IP.IP TLV.TLV ALP.ALP |
isPassthrough: |
선택사항:DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW |
필터링된 프로토콜 하위 스트림은 필터 체인에서 다음 필터를 공급합니다. | N/A |
isPassthrough: |
필수:DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 권장: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER |
이벤트 및 내부 일정에 따라Filter.read(buffer, offset, adjustedSize) 를 1회 이상 실행합니다.데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다. |
프로토콜 헤더가 있는 필터링된 프로토콜 하위 스트림이 FMQ에 채워집니다. | |
IP.PAYLOAD_THROUGH TLV.PAYLOAD_THROUGH ALP.PAYLOAD_THROUGH |
N/A | 선택사항:DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW |
필터링된 프로토콜 페이로드는 필터 체인에서 다음 필터를 공급합니다. | N/A |
필터를 사용하여 PSI/SI를 빌드하는 흐름의 예
그림 10. PSI/SI 빌드 흐름
필터를 엽니다.
Filter filter = tuner.openFilter( Filter.TYPE_TS, Filter.SUBTYPE_SECTION, /* bufferSize */1000, executor, filterCallback );
필터를 구성하고 시작합니다.
Settings settings = SectionSettingsWithTableInfo .builder(Filter.TYPE_TS) .setTableId(2) .setVersion(1) .setCrcEnabled(true) .setRaw(false) .setRepeat(false) .build(); FilterConfiguration config = TsFilterConfiguration .builder() .setTpid(10) .setSettings(settings) .build(); filter.configure(config); filter.start();
SectionEvent
를 처리합니다.FilterCallback filterCallback = new FilterCallback() { @Override public void onFilterEvent(Filter filter, FilterEvent[] events) { for (FilterEvent event : events) { if (event instanceof SectionEvent) { SectionEvent sectionEvent = (SectionEvent) event; int tableId = sectionEvent.getTableId(); int version = sectionEvent.getVersion(); int dataLength = sectionEvent.getDataLength(); int sectionNumber = sectionEvent.getSectionNumber(); filter.read(buffer, 0, dataLength); } } } };
필터에서 MediaEvent를 사용하는 흐름의 예
그림 11. 필터에서 MediaEvent를 사용하는 흐름
- A/V 필터를 열고 구성하여 시작합니다.
MediaEvent
를 처리합니다.MediaEvent
를 수신합니다.- 선형 블록을
codec
의 대기열에 추가합니다. - 데이터가 사용되면 A/V 핸들을 해제합니다.
Android.media.tv.tuner.dvr
DvrRecorder
는 다음 녹화 메서드를 제공합니다.
configure
attachFilter
detachFilter
start
flush
stop
setFileDescriptor
write
DvrPlayback
은 다음 재생 메서드를 제공합니다.
configure
start
flush
stop
setFileDescriptor
read
DvrSettings
는 DvrRecorder
와 DvrPlayback
을 구성하는 데 사용됩니다.
OnPlaybackStatusChangedListener
와 OnRecordStatusChangedListener
는 DVR 인스턴스의 상태를 보고하는 데 사용됩니다.
녹화 시작 흐름 예
그림 12. 녹화 시작 흐름
DvrRecorder
를 열고 구성하여 시작합니다.DvrRecorder recorder = openDvrRecorder(/* bufferSize */ 1000, executor, listener); DvrSettings dvrSettings = DvrSettings .builder() .setDataFormat(DvrSettings.DATA_FORMAT_TS) .setLowThreshold(100) .setHighThreshold(900) .setPacketSize(188) .build(); recorder.configure(dvrSettings); recorder.attachFilter(filter); recorder.setFileDescriptor(fd); recorder.start();
RecordEvent
를 수신하고 색인 정보를 검색합니다.FilterCallback filterCallback = new FilterCallback() { @Override public void onFilterEvent(Filter filter, FilterEvent[] events) { for (FilterEvent event : events) { if (event instanceof TsRecordEvent) { TsRecordEvent recordEvent = (TsRecordEvent) event; int tsMask = recordEvent.getTsIndexMask(); int scMask = recordEvent.getScIndexMask(); int packetId = recordEvent.getPacketId(); long dataLength = recordEvent.getDataLength(); // handle the masks etc. } } } };
OnRecordStatusChangedListener
를 초기화하고 녹화 데이터를 저장합니다.OnRecordStatusChangedListener listener = new OnRecordStatusChangedListener() { @Override public void onRecordStatusChanged(int status) { // a customized way to consume data efficiently by using status as a hint. if (status == Filter.STATUS_DATA_READY) { recorder.write(size); } } };
튜너 HAL
튜너 HAL은 HIDL을 따르고 프레임워크와 공급업체 하드웨어 간의 인터페이스를 정의합니다. 공급업체는 인터페이스를 사용하여 튜너 HAL을 구현하고 프레임워크는 인터페이스를 사용하여 튜너 HAL 구현과 통신합니다.
모듈
튜너 HAL 1.0
모듈 | 기본 제어 | 모듈별 제어 | HAL 파일 |
---|---|---|---|
ITuner |
N/A | frontend(open, getIds, getInfo) , openDemux ,
openDescrambler , openLnb ,
getDemuxCaps |
ITuner.hal |
IFrontend |
setCallback , getStatus , close | tune , stopTune , scan ,
stopScan , setLnb |
IFrontend.hal IFrontendCallback.hal |
IDemux |
close |
setFrontendDataSource , openFilter , openDvr , getAvSyncHwId ,
getAvSyncTime , connect / disconnectCiCam |
IDemux.hal |
IDvr |
close , start , stop , configure |
attach/detachFilters , flush , getQueueDesc |
IDvr.hal IDvrCallback.hal |
IFilter |
close , start , stop , configure , getId |
flush , getQueueDesc , releaseAvHandle , setDataSource |
IFilter.hal IFilterCallback.hal |
ILnb |
close , setCallback |
setVoltage , setTone , setSatellitePosition , sendDiseqcMessage |
ILnb.hal ILnbCallback.hal |
IDescrambler |
close |
setDemuxSource , setKeyToken ,
addPid , removePid |
IDescrambler.hal |
튜너 HAL 1.1(튜너 HAL 1.0에서 파생됨)
모듈 | 기본 제어 | 모듈별 제어 | HAL 파일 |
---|---|---|---|
ITuner |
해당 사항 없음 | getFrontendDtmbCapabilities |
@1.1::ITuner.hal |
IFrontend |
tune_1_1 , scan_1_1 , getStatusExt1_1 |
link/unlinkCiCam |
@1.1::IFrontend.hal @1.1::IFrontendCallback.hal |
IFilter |
getStatusExt1_1 |
configureIpCid , configureAvStreamType , getAvSharedHandle , configureMonitorEvent |
@1.1::IFilter.hal @1.1::IFilterCallback.hal |
그림 13. 튜너 HAL 모듈 간 상호작용 다이어그램
필터 연결
튜너 HAL은 여러 레이어의 다른 필터에 필터를 연결할 수 있도록 필터 연결을 지원합니다. 필터는 아래의 규칙을 따릅니다.
- 필터는 트리로 연결되며 가까운 경로는 허용되지 않습니다.
- 루트 노드는 demux입니다.
- 필터는 독립적으로 작동합니다.
- 모든 필터는 데이터를 가져오기 시작합니다.
- 필터 연결은 마지막 필터에서 플러시됩니다.
아래의 코드 블록과 그림 14는 여러 레이어를 필터링하는 예를 보여 줍니다.
demuxCaps = ITuner.getDemuxCap;
If (demuxCaps[IP][MMTP] == true) {
ipFilter = ITuner.openFilter(<IP, ..>)
mmtpFilter1 = ITuner.openFilter(<MMTP ..>)
mmtpFilter2 = ITuner.openFilter(<MMTP ..>)
mmtpFilter1.setDataSource(<ipFilter>)
mmtpFilter2.setDataSource(<ipFilter>)
}
그림 14. 여러 레이어의 필터 연결 흐름 다이어그램
튜너 리소스 관리자
튜너 리소스 관리자(TRM) 전에는 두 앱 간에 전환하려면 동일한 튜너 하드웨어가 필요했습니다. TV 입력 프레임워크(TIF)는 '먼저 획득 우선' 메커니즘을 사용했습니다. 즉, 리소스를 먼저 가져오는 앱이 그 리소스를 유지합니다. 그러나 이 메커니즘은 일부 복잡한 사용 사례에 적합하지 않을 수 있습니다.
TRM은 시스템 서비스로 실행되어 앱의 튜너, TVInput
, CAS 하드웨어 리소스를 관리합니다. TRM은 '포그라운드 우선' 메커니즘을 사용하고 이 메커니즘은 앱의 포그라운드나 백그라운드 상태와 사용 사례 유형에 기반하여 앱의 우선순위를 계산합니다. TRM은 우선순위에 따라 리소스를 부여하거나 취소합니다. TRM은 방송, OTT, DVR의 ATV 리소스 관리를 중앙 집중식으로 처리합니다.
TRM 인터페이스
TRM은 튜너 프레임워크, MediaCas
, TvInputHardwareManager
의 ITunerResourceManager.aidl
에 있는 AIDL 인터페이스를 노출하여 리소스를 등록하거나 요청하거나 해제합니다.
클라이언트 관리용 인터페이스는 아래에 나와 있습니다.
registerClientProfile(in ResourceClientProfile profile, IResourcesReclaimListener listener, out int[] clientId)
unregisterClientProfile(in int clientId)
리소스를 요청하고 해제하는 인터페이스는 아래에 나와 있습니다.
requestFrontend(TunerFrontendRequest request, int[] frontendHandle)
/releaseFrontend
requestDemux(TunerDemuxRequest request, int[] demuxHandle)
/releaseDemux
requestDescrambler(TunerDescramblerRequest request, int[] descramblerHandle)
/releaseDescrambler
requestCasSession(CasSessionRequest request, int[] casSessionHandle)
/releaseCasSession
requestLnb(TunerLnbRequest request, int[] lnbHandle)
/releaseLnb
클라이언트 및 요청 클래스는 아래에 나와 있습니다.
ResourceClientProfile
ResourcesReclaimListener
TunerFrontendRequest
TunerDemuxRequest
TunerDescramblerRequest
CasSessionRequest
TunerLnbRequest
클라이언트 우선순위
TRM은 클라이언트 프로필의 매개변수와 구성 파일의 우선순위 값을 사용하여 클라이언트의 우선순위를 계산합니다. 우선순위는 클라이언트의 임의 우선순위 값으로 업데이트될 수도 있습니다.
클라이언트 프로필의 매개변수
TRM은 mTvInputSessionId
의 프로세스 ID를 검색하여 앱이 포그라운드 앱인지 또는 백그라운드 앱인지 판단합니다. mTvInputSessionId
나 TvInputService.onCreateSession
, TvInputService.onCreateRecordingSession
을 만들면 TIS 세션이 초기화됩니다.
mUseCase
는 세션의 사용 사례를 나타냅니다. 사전 정의된 사용 사례는 아래에 나와 있습니다.
TvInputService.PriorityHintUseCaseType {
PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK
PRIORITY_HINT_USE_CASE_TYPE_LIVE
PRIORITY_HINT_USE_CASE_TYPE_RECORD,
PRIORITY_HINT_USE_CASE_TYPE_SCAN,
PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND
}
구성 파일
기본 구성 파일
아래의 기본 구성 파일은 사전 정의된 사용 사례의 우선순위 값을 제공합니다. 사용자는 맞춤설정된 구성 파일을 사용하여 값을 변경할 수 있습니다.
사용 사례 | 포그라운드 | 백그라운드 |
---|---|---|
LIVE |
490 | 400 |
PLAYBACK |
480 | 300 |
RECORD |
600 | 500 |
SCAN |
450 | 200 |
BACKGROUND |
180 | 100 |
맞춤설정된 구성 파일
공급업체는 구성 파일 /vendor/etc/tunerResourceManagerUseCaseConfig.xml
을 맞춤설정할 수 있습니다. 이 파일은 사용 사례 유형과 사용 사례 우선순위 값을 추가하거나 삭제, 업데이트하는 데 사용됩니다.
맞춤설정된 파일은 platform/hardware/interfaces/tv/tuner/1.0/config/tunerResourceManagerUseCaseConfigSample.xml
을 템플릿으로 사용할 수 있습니다.
예를 들어 새로운 공급업체 사용 사례는 VENDOR_USE_CASE__[A-Z0-9]+, [0 - 1000]
입니다.
형식은 platform/hardware/interfaces/tv/tuner/1.0/config/tunerResourceManagerUseCaseConfig.xsd
를 따라야 합니다.
임의 우선순위 값과 nice 값
TRM은 클라이언트가 임의 우선순위 값과 nice 값을 업데이트할 수 있도록 updateClientPriority
를 제공합니다.
임의 우선순위 값은 사용 사례 유형과 세션 ID에서 계산된 우선순위 값을 덮어씁니다.
nice 값은 클라이언트의 동작이 다른 클라이언트와 충돌할 때 얼마나 유연한지 나타냅니다. nice 값은 우선순위 값이 까다로운 클라이언트와 비교되기 전에 클라이언트의 우선순위 값을 낮춥니다.
회수 메커니즘
아래의 다이어그램은 리소스 충돌이 발생할 때 리소스가 회수되고 할당되는 방식을 보여 줍니다.
그림 15. 튜너 리소스 간 충돌 시 회수 메커니즘 다이어그램