튜너 프레임워크

Android 11 이상에서는 Android 튜너 프레임워크를 사용하여 A/V 콘텐츠를 제공할 수 있습니다. 프레임워크는 공급업체의 하드웨어 파이프라인을 사용하므로 저사양 및 고사양 SoC에 모두 적합합니다. 프레임워크는 신뢰할 수 있는 실행 환경(TEE)과 보안 미디어 경로(SMP)로 보호되는 A/V 콘텐츠를 안전하게 제공하는 방법을 제공하므로 고도로 제한된 콘텐츠 보호 환경에서 사용할 수 있습니다.

튜너와 Android CAS 간에 표준화된 인터페이스로 인해 튜너 공급업체와 CAS 공급업체 간 통합이 더 빨라집니다. 튜너 인터페이스는 MediaCodecAudioTrack과 함께 작동하여 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을 MediaCodecAudioTrack 등의 다른 프레임워크 구성요소와 통합하는 함수를 포함합니다.

튜너 자바 클래스와 네이티브 클래스가 만들어집니다.

  • Tuner Java API를 사용하면 앱이 공개 API를 통해 튜너 HAL에 액세스할 수 있습니다.
  • 네이티브 클래스를 사용하면 권한을 제어할 수 있고 튜너 HAL로 대량의 녹화나 재생 데이터를 처리할 수 있습니다.
  • 네이티브 튜너 모듈은 튜너 자바 클래스와 튜너 HAL 사이의 브리지입니다.

TRM 클래스가 만들어집니다.

  • 프런트엔드, LNB, CAS 세션, TV 입력 HAL의 TV 입력 장치와 같은 제한된 튜너 리소스를 관리합니다.
  • 앱에서 부족한 리소스를 회수하는 규칙을 적용합니다. 기본 규칙은 포그라운드 우선입니다.

미디어 CAS와 CAS HAL은 아래 기능으로 개선되었습니다.

  • 다양한 사용과 알고리즘의 CAS 세션을 엽니다.
  • CICAM 삭제 및 삽입과 같은 동적 CAS 시스템을 지원합니다.
  • 키 토큰을 제공하여 튜너 HAL과 통합합니다.

MediaCodecAudioTrack이 아래 기능으로 개선되었습니다.

  • 보안 A/V 메모리를 콘텐츠 입력으로 사용합니다.
  • 터널링된 재생에서 하드웨어 A/V 동기화를 실행하도록 구성되었습니다.
  • ES_payload 및 패스 스루 모드 지원을 구성했습니다.

튜너 HAL의 전반적인 디자인

그림 2. 튜너 HAL 내의 구성요소 다이어그램

전체 워크플로

아래 다이어그램은 실시간 방송 재생의 호출 시퀀스를 보여 줍니다.

설정

실시간 방송 재생의 설정 시퀀스 다이어그램

그림 3. 실시간 방송 재생의 설정 시퀀스

A/V 처리

실시간 방송 재생의 A/V 처리 다이어그램

그림 4. 실시간 방송 재생의 A/V 처리

스크램블링된 콘텐츠 처리

실시간 방송 재생의 스크램블링된 콘텐츠 처리 다이어그램

그림 5. 실시간 방송 재생의 스크램블링된 콘텐츠 처리

A/V 데이터 처리

실시간 방송 재생의 A/V 데이터 처리 다이어그램

그림 6. 실시간 방송 재생의 A/V 처리

Tuner SDK API

Tuner SDK API는 튜너 JNI, 튜너 HAL, TunerResourceManager와의 상호작용을 처리합니다. TIS 앱은 Tuner SDK API를 사용하여 튜너 리소스와 하위 구성요소(필터, 디스크램블러 등)에 액세스합니다. 프런트엔드와 demux는 내부 구성요소입니다.

Tuner SDK API의 흐름 다이어그램

그림 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

Tuner SDK API 패키지의 흐름 다이어그램

그림 8. Tuner SDK API 패키지

Android.media.tv.tuner

튜너 패키지는 튜너 프레임워크를 사용하기 위한 진입점입니다. TIS 앱은 패키지를 통해 초기 설정과 콜백을 지정하여 리소스 인스턴스를 초기화하고 가져옵니다.

  • tuner(): useCasesessionId 매개변수를 지정하여 튜너 인스턴스를 초기화합니다.
  • 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()를 호출하여 검색을 일시중지하거나 종료할 수 있습니다.

TIS 검색 프로세스의 흐름 다이어그램

그림 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:
true
필수:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

권장:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
이벤트 및 내부 일정에 따라
Filter.read(buffer, offset, adjustedSize)를 1회 이상 실행합니다.

데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다.
구성된 세션 패키지 하나가 다른 세션 패키지로 FMQ에 채워집니다.
isRaw:
false
필수:
DemuxFilterEvent::DemuxFilterSectionEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

선택사항:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++
Filter.read(buffer, offset, DemuxFilterSectionEven[i].size)


데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다.
TS.PES isRaw:
true
필수:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

권장:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
이벤트 및 내부 일정에 따라
Filter.read(buffer, offset, adjustedSize)를 1회 이상 실행합니다.

데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다.
구성된 PES 패키지 하나가 다른 PES 패키지로 FMQ에 채워집니다.
isRaw:
false
필수:
DemuxFilterEvent::DemuxFilterPesEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

선택사항:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++
Filter.read(buffer, offset, DemuxFilterPesEven[i].size)


데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다.
MMTP.PES isRaw:
true
필수:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

권장:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
이벤트 및 내부 일정에 따라
Filter.read(buffer, offset, adjustedSize)를 1회 이상 실행합니다.

데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다.
구성된 MFU 패키지 하나가 다른 MFU 패키지로 FMQ에 채워집니다.
isRaw:
false
필수:
DemuxFilterEvent::DemuxFilterPesEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

선택사항:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++
Filter.read(buffer, offset, DemuxFilterPesEven[i].size)


데이터는 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:
true
선택사항:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
클라이언트는 DemuxFilterStatus::DATA_READY를 수신한 후 MediaCodec을 시작할 수 있습니다.
클라이언트는 DemuxFilterStatus::DATA_OVERFLOW를 수신한 후 Filter.flush를 호출할 수 있습니다.
N/A
isPassthrough:
false
필수:
DemuxFilterEvent::DemuxFilterMediaEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

선택사항:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
MediaCodec 사용 방법:
for i=0; i<n; i++
linearblock = MediaEvent[i].getLinearBlock();
codec.startQueueLinearBlock(linearblock)
linearblock.recycle()


AudioTrack의 직접 오디오 사용 방법:
for i=0; i<n; i++
audioHandle = MediaEvent[i].getAudioHandle();
audiotrack.write(encapsulated(audiohandle))
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++
DemuxFilterTsRecordEvent[i];


녹화된 콘텐츠의 경우 RecordStatus::*와 내부 일정에 따라 다음 중 하나를 실행합니다.
  • DvrRecord.write(adustedSize)를 저장소에 1회 이상 실행합니다.
    데이터는 HAL의 MQ에서 저장소로 전송됩니다.
  • DvrRecord.write(buffer, adustedSize)를 버퍼에 1회 이상 실행합니다.
    데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다.
색인 데이터의 경우: 이벤트 페이로드에서 가져옵니다.

녹화된 콘텐츠의 경우: 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++
DemuxFilterTemiEvent[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++
DemuxFilterMmtpRecordEvent[i];


녹화된 콘텐츠의 경우 RecordStatus::* 및 내부 일정에 따라 다음 중 하나를 실행합니다.
  • DvrRecord.write(adjustedSize)를 저장소에 1회 이상 실행합니다.
    데이터는 HAL의 MQ에서 저장소로 전송됩니다.
  • DvrRecord.write(buffer, adjustedSize)를 버퍼에 1회 이상 실행합니다.
    데이터는 HAL의 MQ에서 클라이언트 버퍼로 복사됩니다.
색인 데이터의 경우: 이벤트 페이로드에서 가져옵니다.

녹화된 콘텐츠의 경우: 녹화된 다중 스트림이 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:
true
선택사항:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
필터링된 프로토콜 하위 스트림은 필터 체인에서 다음 필터를 공급합니다. N/A
isPassthrough:
false
필수:
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를 빌드하는 흐름의 예

필터를 사용하여 PSI/SI를 빌드하는 흐름의 예

그림 10. PSI/SI 빌드 흐름

  1. 필터를 엽니다.

    Filter filter = tuner.openFilter(
      Filter.TYPE_TS,
      Filter.SUBTYPE_SECTION,
      /* bufferSize */1000,
      executor,
      filterCallback
    );
    
  2. 필터를 구성하고 시작합니다.

    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();
    
  3. 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를 사용하는 흐름의 예

필터에서 MediaEvent를 사용하는 흐름의 예.

그림 11. 필터에서 MediaEvent를 사용하는 흐름

  1. A/V 필터를 열고 구성하여 시작합니다.
  2. MediaEvent를 처리합니다.
  3. MediaEvent를 수신합니다.
  4. 선형 블록을 codec의 대기열에 추가합니다.
  5. 데이터가 사용되면 A/V 핸들을 해제합니다.

Android.media.tv.tuner.dvr

DvrRecorder는 다음 녹화 메서드를 제공합니다.

  • configure
  • attachFilter
  • detachFilter
  • start
  • flush
  • stop
  • setFileDescriptor
  • write

DvrPlayback은 다음 재생 메서드를 제공합니다.

  • configure
  • start
  • flush
  • stop
  • setFileDescriptor
  • read

DvrSettingsDvrRecorderDvrPlayback을 구성하는 데 사용됩니다. OnPlaybackStatusChangedListenerOnRecordStatusChangedListener는 DVR 인스턴스의 상태를 보고하는 데 사용됩니다.

녹화 시작 흐름 예

녹화 시작 흐름 예

그림 12. 녹화 시작 흐름

  1. 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();
    
  2. 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. }
          }
        }
    };
    
  3. 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, closetune, 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

튜너 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, TvInputHardwareManagerITunerResourceManager.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를 검색하여 앱이 포그라운드 앱인지 또는 백그라운드 앱인지 판단합니다. mTvInputSessionIdTvInputService.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. 튜너 리소스 간 충돌 시 회수 메커니즘 다이어그램