พัฒนาบริการอินพุตทีวี

บริการอินพุตทีวีแสดงถึงแหล่งที่มาของสตรีมสื่อ และช่วยให้คุณนำเสนอเนื้อหาสื่อในรูปแบบทีวีเชิงเส้นแบบออกอากาศเป็นช่องและรายการ บริการอินพุตทีวีช่วยให้คุณสามารถควบคุมโดยผู้ปกครอง ข้อมูลคู่มือรายการทีวี และการจัดประเภทเนื้อหา บริการอินพุตทีวีทำงานร่วมกับแอป 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 ของระบบและจัดการ การโต้ตอบกับอินพุตทีวีและแอป

นอกจากนี้ คุณยังต้องทำสิ่งต่อไปนี้

  1. ประกาศบริการอินพุตทีวีในไฟล์ Manifest ตามที่ อธิบายไว้ในหัวข้อประกาศบริการอินพุตทีวีในไฟล์ Manifest
  2. สร้างไฟล์ข้อมูลเมตาของบริการ
  3. สร้างและลงทะเบียนข้อมูลช่องและรายการ
  4. สร้างกิจกรรมการตั้งค่า

กำหนดบริการอินพุตทีวี

สำหรับบริการ ให้ขยายคลาส 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 ของระบบดูได้ผ่านอินพุตทีวี ดังที่แสดงในบทเรียนถัดไปเรื่องสร้าง และอัปเดตข้อมูลช่อง

ข้อมูลอ้างอิงเพิ่มเติม