บริการอินพุตทีวีแสดงถึงแหล่งที่มาของสตรีมสื่อ และช่วยให้คุณนำเสนอเนื้อหาสื่อในรูปแบบทีวีเชิงเส้นแบบออกอากาศเป็นช่องและรายการ บริการอินพุตทีวีช่วยให้คุณสามารถควบคุมโดยผู้ปกครอง ข้อมูลคู่มือรายการทีวี และการจัดประเภทเนื้อหา บริการอินพุตทีวีทำงานร่วมกับแอป TV ของระบบ Android ซึ่งแอปนี้จะควบคุมและนำเสนอเนื้อหาช่องในทีวี แอป TV ของระบบได้รับการพัฒนาขึ้นสำหรับอุปกรณ์โดยเฉพาะและแอปของบุคคลที่สามจะแก้ไขไม่ได้ ดูข้อมูลเพิ่มเติมเกี่ยวกับสถาปัตยกรรม TV Input Framework (TIF) และคอมโพเนนต์ต่างๆ ได้ที่ TV Input Framework
สร้างบริการอินพุตทีวีโดยใช้ TIF Companion Library
TIF Companion Library เป็นเฟรมเวิร์กที่ให้การติดตั้งใช้งานที่ขยายได้ของฟีเจอร์บริการอินพุตทีวีทั่วไป โดยมีไว้ให้ OEM ใช้เพื่อสร้างช่องสำหรับ Android 5.0 (API ระดับ 21) ถึง Android 7.1 (API ระดับ 25) เท่านั้น
อัปเดตโปรเจ็กต์
TIF Companion Library พร้อมให้ OEM ใช้งานแบบเดิมในที่เก็บ androidtv-sample-inputs ดูที่เก็บดังกล่าวเพื่อดูตัวอย่างวิธีรวมไลบรารีไว้ในแอป
ประกาศบริการอินพุตทีวีในไฟล์ Manifest
แอปของคุณต้องมีบริการที่เข้ากันได้กับ TvInputService-
ซึ่งระบบใช้เพื่อเข้าถึงแอปของคุณ TIF
Companion Library มีคลาส BaseTvInputService ซึ่ง
ให้การติดตั้งใช้งานเริ่มต้นของ TvInputService
ที่คุณปรับแต่งได้ สร้างคลาสย่อยของ BaseTvInputService และประกาศคลาสย่อยในไฟล์ Manifest เป็นบริการ
ภายในประกาศไฟล์ Manifest ให้ระบุสิทธิ์ BIND_TV_INPUT เพื่ออนุญาตให้บริการเชื่อมต่ออินพุตทีวีกับระบบ บริการของระบบจะทำการผูกและมีสิทธิ์ BIND_TV_INPUT
แอป TV ของระบบจะส่งคำขอไปยังบริการอินพุตทีวีผ่านอินเทอร์เฟซ TvInputManager
ในการประกาศบริการ ให้ใส่ตัวกรอง Intent ที่ระบุ TvInputService เป็นการดำเนินการที่จะทำกับ Intent นอกจากนี้ ให้ประกาศข้อมูลเมตาของบริการเป็นทรัพยากร XML แยกต่างหาก การประกาศบริการ ตัวกรอง Intent และการประกาศข้อมูลเมตาของบริการแสดงอยู่ในตัวอย่างต่อไปนี้
<service android:name=".rich.RichTvInputService" android:label="@string/rich_input_label" android:permission="android.permission.BIND_TV_INPUT"> <!-- Required filter used by the system to launch our account service. --> <intent-filter> <action android:name="android.media.tv.TvInputService" /> </intent-filter> <!-- An XML file which describes this input. This provides pointers to the RichTvInputSetupActivity to the system/TV app. --> <meta-data android:name="android.media.tv.input" android:resource="@xml/richtvinputservice" /> </service>
กำหนดข้อมูลเมตาของบริการในไฟล์ XML แยกต่างหาก ไฟล์ XML ข้อมูลเมตาของบริการต้องมีอินเทอร์เฟซการตั้งค่าที่อธิบายการกำหนดค่าเริ่มต้นและการสแกนช่องของอินพุตทีวี นอกจากนี้ ไฟล์ข้อมูลเมตาควรมีแฟล็กที่ระบุว่าผู้ใช้บันทึกเนื้อหาได้หรือไม่ ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีรองรับการบันทึกเนื้อหาในแอปได้ที่หัวข้อ รองรับการบันทึกเนื้อหา
ไฟล์ข้อมูลเมตาของบริการจะอยู่ในไดเรกทอรีทรัพยากร XML ของแอปและต้องมีชื่อตรงกับชื่อทรัพยากรที่คุณประกาศไว้ในไฟล์ Manifest เมื่อใช้รายการไฟล์ Manifest จากตัวอย่างก่อนหน้า คุณจะต้องสร้างไฟล์ XML ที่ res/xml/richtvinputservice.xml โดยมีเนื้อหาดังนี้
<?xml version="1.0" encoding="utf-8"?> <tv-input xmlns:android="http://schemas.android.com/apk/res/android" android:canRecord="true" android:setupActivity="com.example.android.sampletvinput.rich.RichTvInputSetupActivity" />
กำหนดช่องและสร้างกิจกรรมการตั้งค่า
บริการอินพุตทีวีต้องกำหนดช่องอย่างน้อย 1 ช่องที่ผู้ใช้เข้าถึงผ่านแอป TV ของระบบ คุณควรลงทะเบียนช่องในฐานข้อมูลของระบบและระบุกิจกรรมการตั้งค่าที่ระบบเรียกใช้เมื่อไม่พบช่องสำหรับแอปของคุณ
ขั้นแรก ให้เปิดใช้แอปเพื่ออ่านและเขียนข้อมูลลงในคู่มือรายการอิเล็กทรอนิกส์ (EPG) ของระบบ ซึ่งข้อมูลนี้รวมถึงช่องและรายการที่ผู้ใช้ดูได้ หากต้องการเปิดใช้แอปให้ดำเนินการเหล่านี้และซิงค์กับ EPG หลังจากรีสตาร์ทอุปกรณ์ ให้เพิ่มองค์ประกอบต่อไปนี้ลงในไฟล์ Manifest ของแอป
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED "/>
เพิ่มองค์ประกอบต่อไปนี้เพื่อให้แน่ใจว่าแอปของคุณจะปรากฏใน Google Play Store เป็นแอปที่ให้บริการช่องเนื้อหาใน Android TV
<uses-feature android:name="android.software.live_tv" android:required="true" />
จากนั้นสร้างคลาสที่ขยายคลาส EpgSyncJobService คลาสแอบสแตรกต์นี้ช่วยให้คุณสร้างบริการงานที่สร้างและอัปเดตช่องในฐานข้อมูลของระบบได้
ในคลาสย่อย ให้สร้างและแสดงรายการช่องทั้งหมดใน getChannels หากช่องมาจากไฟล์ XMLTV ให้ใช้คลาส XmlTvParser ไม่เช่นนั้น ให้สร้างช่องแบบเป็นโปรแกรมโดยใช้คลาส Channel.Builder
สำหรับแต่ละช่อง ระบบจะเรียกใช้ getProgramsForChannel เมื่อต้องการรายการรายการที่ดูได้ภายในช่วงเวลาที่กำหนดในช่อง แสดงรายการออบเจ็กต์ Program สำหรับช่อง ใช้คลาส XmlTvParser เพื่อรับรายการจากไฟล์ XMLTV หรือสร้างรายการแบบเป็นโปรแกรมโดยใช้คลาส Program.Builder
สำหรับออบเจ็กต์ Program แต่ละรายการ ให้ใช้ออบเจ็กต์ InternalProviderData เพื่อตั้งค่าข้อมูลรายการ เช่น ประเภทวิดีโอของรายการ หากคุณมีรายการจำนวนจำกัดที่ต้องการให้ช่องเล่นซ้ำเป็นลูป ให้ใช้วิธี InternalProviderData.setRepeatable โดยตั้งค่าเป็น true เมื่อตั้งค่าข้อมูลเกี่ยวกับรายการ
หลังจากติดตั้งใช้งานบริการงานแล้ว ให้เพิ่มบริการลงในไฟล์ Manifest ของแอป
<service android:name=".sync.SampleJobService" android:permission="android.permission.BIND_JOB_SERVICE" android:exported="true" />
สุดท้าย ให้สร้างกิจกรรมการตั้งค่า กิจกรรมการตั้งค่าควรมีวิธีซิงค์ข้อมูลช่องและรายการ วิธีหนึ่งคือให้ผู้ใช้ดำเนินการโดยใช้ UI ในกิจกรรม หรือคุณอาจให้แอปดำเนินการโดยอัตโนมัติเมื่อกิจกรรมเริ่มต้น เมื่อกิจกรรมการตั้งค่าต้องซิงค์ข้อมูลช่องและรายการ แอปควรเริ่มบริการงานดังนี้
Kotlin
val inputId = getActivity().intent.getStringExtra(TvInputInfo.EXTRA_INPUT_ID) EpgSyncJobService.cancelAllSyncRequests(getActivity()) EpgSyncJobService.requestImmediateSync( getActivity(), inputId, ComponentName(getActivity(), SampleJobService::class.java) )
Java
String inputId = getActivity().getIntent().getStringExtra(TvInputInfo.EXTRA_INPUT_ID); EpgSyncJobService.cancelAllSyncRequests(getActivity()); EpgSyncJobService.requestImmediateSync(getActivity(), inputId, new ComponentName(getActivity(), SampleJobService.class));
ใช้วิธี requestImmediateSync เพื่อซิงค์บริการงาน ผู้ใช้ต้องรอให้การซิงค์เสร็จสิ้น ดังนั้นคุณควรกำหนดระยะเวลาคำขอให้ค่อนข้างสั้น
ใช้วิธี setUpPeriodicSync เพื่อให้บริการงานซิงค์ข้อมูลช่องและรายการเป็นระยะๆ ในเบื้องหลัง
Kotlin
EpgSyncJobService.setUpPeriodicSync( context, inputId, ComponentName(context, SampleJobService::class.java) )
Java
EpgSyncJobService.setUpPeriodicSync(context, inputId, new ComponentName(context, SampleJobService.class));
TIF Companion Library มีวิธี requestImmediateSync ที่โอเวอร์โหลดเพิ่มเติม ซึ่งช่วยให้คุณระบุระยะเวลาของข้อมูลช่องที่จะซิงค์เป็นมิลลิวินาทีได้ วิธีเริ่มต้นจะซิงค์ข้อมูลช่องเป็นเวลา 1 ชั่วโมง
นอกจากนี้ TIF Companion Library ยังมีวิธี setUpPeriodicSync ที่โอเวอร์โหลดเพิ่มเติม ซึ่งช่วยให้คุณระบุระยะเวลาของข้อมูลช่องที่จะซิงค์และความถี่ในการซิงค์เป็นระยะๆ ได้ วิธีเริ่มต้นจะซิงค์ข้อมูลช่อง 48 ชั่วโมงทุกๆ 12 ชั่วโมง
ดูรายละเอียดเพิ่มเติมเกี่ยวกับข้อมูลช่องและ EPG ได้ที่ Work with channel data.
จัดการคำขอการปรับและเล่นสื่อ
เมื่อผู้ใช้เลือกช่องที่เฉพาะเจาะจง แอป TV ของระบบจะใช้ Session ที่แอปของคุณสร้างขึ้นเพื่อปรับไปยังช่องที่ขอและเล่นเนื้อหา TIF Companion Library มีคลาสหลายคลาสที่คุณขยายได้เพื่อจัดการการเรียกช่องและเซสชันจากระบบ
คลาสย่อย BaseTvInputService จะสร้างเซสชันที่จัดการคำขอการปรับ ลบล้างวิธี onCreateSession สร้างเซสชันที่ขยายจากคลาส BaseTvInputService.Session และเรียกใช้ super.sessionCreated ด้วยเซสชันใหม่ ในตัวอย่างต่อไปนี้ onCreateSession จะแสดงออบเจ็กต์ RichTvInputSessionImpl ที่ขยาย BaseTvInputService.Session
Kotlin
override fun onCreateSession(inputId: String): Session = RichTvInputSessionImpl(this, inputId).apply { setOverlayViewEnabled(true) }
Java
@Override public final Session onCreateSession(String inputId) { RichTvInputSessionImpl session = new RichTvInputSessionImpl(this, inputId); session.setOverlayViewEnabled(true); return session; }
เมื่อผู้ใช้ใช้แอป TV ของระบบเพื่อเริ่มดูช่องใดช่องหนึ่ง ระบบจะเรียกใช้วิธี onPlayChannel ของเซสชัน ลบล้างวิธีนี้หากคุณต้องทำการเริ่มต้นช่องพิเศษก่อนที่รายการจะเริ่มเล่น
จากนั้นระบบจะรับรายการที่กำหนดเวลาไว้ในปัจจุบันและเรียกใช้วิธี onPlayProgram ของเซสชัน โดยระบุข้อมูลรายการและเวลาเริ่มต้นเป็นมิลลิวินาที ใช้อินเทอร์เฟซ TvPlayer เพื่อเริ่มเล่นรายการ
รหัสเครื่องเล่นมีเดียควรติดตั้งใช้งาน TvPlayer เพื่อจัดการเหตุการณ์การเล่นที่เฉพาะเจาะจง คลาส TvPlayer จะจัดการฟีเจอร์ต่างๆ เช่น การควบคุมการเปลี่ยนเวลาโดยไม่เพิ่มความซับซ้อนให้กับการติดตั้งใช้งาน BaseTvInputService
ในวิธี getTvPlayer ของเซสชัน ให้แสดง
มีเดียเพลเยอร์ที่ติดตั้งใช้งาน TvPlayer แอปตัวอย่าง
บริการอินพุตทีวีติดตั้งใช้งานมีเดียเพลเยอร์ที่ใช้
ExoPlayer
สร้างบริการอินพุตทีวีโดยใช้ TV Input Framework
หากบริการอินพุตทีวีใช้ TIF Companion Library ไม่ได้ คุณจะต้องติดตั้งใช้งานคอมโพเนนต์ต่อไปนี้
TvInputServiceให้ความพร้อมใช้งานระยะยาวและเบื้องหลังสำหรับ อินพุตทีวีTvInputService.Sessionรักษาสถานะอินพุตทีวีและสื่อสาร กับแอปโฮสต์TvContractอธิบายช่องและรายการที่อินพุตทีวีดูได้TvContract.Channelsแสดงข้อมูลเกี่ยวกับช่องทีวีTvContract.Programsอธิบายรายการทีวีด้วยข้อมูล เช่น ชื่อรายการและเวลาเริ่มต้นTvTrackInfoแสดงแทร็กเสียง วิดีโอ หรือคำบรรยายแทนเสียงTvContentRatingอธิบายการจัดประเภทเนื้อหาและอนุญาตให้ใช้รูปแบบการจัดประเภทเนื้อหาที่กำหนดเองTvInputManagerมี API สำหรับแอป TV ของระบบและจัดการ การโต้ตอบกับอินพุตทีวีและแอป
นอกจากนี้ คุณยังต้องทำสิ่งต่อไปนี้
- ประกาศบริการอินพุตทีวีในไฟล์ Manifest ตามที่ อธิบายไว้ในหัวข้อประกาศบริการอินพุตทีวีในไฟล์ Manifest
- สร้างไฟล์ข้อมูลเมตาของบริการ
- สร้างและลงทะเบียนข้อมูลช่องและรายการ
- สร้างกิจกรรมการตั้งค่า
กำหนดบริการอินพุตทีวี
สำหรับบริการ ให้ขยายคลาส TvInputService การติดตั้งใช้งาน
TvInputService เป็น
บริการที่ผูกไว้ซึ่งบริการของระบบ
เป็นไคลเอ็นต์ที่ผูกกับบริการดังกล่าว วิธีการวงจรการทำงานของบริการที่คุณต้องติดตั้งใช้งานแสดงอยู่ในรูปที่ 1
วิธี onCreate จะเริ่มต้นและเริ่ม HandlerThread ซึ่งมีเธรดกระบวนการแยกจากเธรด UI เพื่อจัดการการดำเนินการที่ระบบเป็นผู้เริ่ม ในตัวอย่างต่อไปนี้ วิธี onCreate จะเริ่มต้น CaptioningManager และเตรียมพร้อมที่จะจัดการการดำเนินการ ACTION_BLOCKED_RATINGS_CHANGED และ ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED การดำเนินการเหล่านี้อธิบาย Intent ของระบบที่เริ่มทำงานเมื่อผู้ใช้เปลี่ยนการตั้งค่าการควบคุมโดยผู้ปกครอง และเมื่อมีการเปลี่ยนแปลงในรายการการจัดประเภทที่ถูกบล็อก
Kotlin
override fun onCreate() { super.onCreate() handlerThread = HandlerThread(javaClass.simpleName).apply { start() } dbHandler = Handler(handlerThread.looper) handler = Handler() captioningManager = getSystemService(Context.CAPTIONING_SERVICE) as CaptioningManager setTheme(android.R.style.Theme_Holo_Light_NoActionBar) sessions = mutableListOf<BaseTvInputSessionImpl>() val intentFilter = IntentFilter().apply { addAction(TvInputManager.ACTION_BLOCKED_RATINGS_CHANGED) addAction(TvInputManager.ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED) } registerReceiver(broadcastReceiver, intentFilter) }
Java
@Override public void onCreate() { super.onCreate(); handlerThread = new HandlerThread(getClass() .getSimpleName()); handlerThread.start(); dbHandler = new Handler(handlerThread.getLooper()); handler = new Handler(); captioningManager = (CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE); setTheme(android.R.style.Theme_Holo_Light_NoActionBar); sessions = new ArrayList<BaseTvInputSessionImpl>(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(TvInputManager .ACTION_BLOCKED_RATINGS_CHANGED); intentFilter.addAction(TvInputManager .ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED); registerReceiver(broadcastReceiver, intentFilter); }
รูปที่ 1 วงจรการทำงานของ TvInputService
ดูข้อมูลเพิ่มเติมเกี่ยวกับการทำงานกับเนื้อหาที่ถูกบล็อกและการควบคุมโดยผู้ปกครองได้ที่หัวข้อ
Control content ดู TvInputManager สำหรับการดำเนินการที่ระบบเป็นผู้เริ่มเพิ่มเติมที่คุณอาจต้องการจัดการในบริการอินพุตทีวี
TvInputService จะสร้าง
TvInputService.Session ที่ติดตั้งใช้งาน Handler.Callback
เพื่อจัดการการเปลี่ยนแปลงสถานะเพลเยอร์ ด้วย
onSetSurface,
นั้น TvInputService.Session จะตั้งค่า Surface ด้วย
เนื้อหาวิดีโอ ดูข้อมูลเพิ่มเติมเกี่ยวกับการทำงานกับ Surface เพื่อแสดงวิดีโอได้ที่หัวข้อ Integrate player with surface
TvInputService.Session จะจัดการเหตุการณ์ onTune เมื่อผู้ใช้เลือกช่อง และแจ้งแอป TV ของระบบเกี่ยวกับการเปลี่ยนแปลงเนื้อหาและข้อมูลเมตาของเนื้อหา วิธีการ notify เหล่านี้อธิบายไว้ใน
Control Content และ Handle track selection
เพิ่มเติมในหลักสูตรนี้
กำหนดกิจกรรมการตั้งค่า
แอป TV ของระบบทำงานร่วมกับกิจกรรมการตั้งค่าที่คุณกำหนดไว้สำหรับอินพุตทีวี กิจกรรมการตั้งค่าเป็นสิ่งจำเป็นและต้องมีบันทึกช่องอย่างน้อย 1 รายการสำหรับฐานข้อมูลของระบบ แอป TV ของระบบจะเรียกใช้กิจกรรมการตั้งค่าเมื่อไม่พบช่องสำหรับอินพุตทีวี
กิจกรรมการตั้งค่าจะอธิบายช่องที่แอป TV ของระบบดูได้ผ่านอินพุตทีวี ดังที่แสดงในบทเรียนถัดไปเรื่องสร้าง และอัปเดตข้อมูลช่อง