Cómo desarrollar un servicio de entrada de TV

Un servicio de entrada de TV representa una fuente de transmisión multimedia y te permite presentar tu contenido multimedia en una la moda lineal y de transmisión de TV, como canales y programas. Con el servicio de entrada de TV, puedes controles parentales, información de la guía de programas y clasificaciones de contenido. Si el servicio de entrada de TV funciona con la app del sistema Android para TV. En última instancia, esta app controla y presenta el contenido del canal en la TV. La app de TV del sistema se desarrolló específicamente para el dispositivo e inmutable por aplicaciones de terceros. Para obtener más información sobre el marco de trabajo de entrada de TV (TIF) arquitectura y sus componentes, consulta Framework de entrada de TV.

Cómo crear un servicio de entrada de TV usando la Biblioteca complementaria del TIF

La Biblioteca complementaria del TIF es un framework implementaciones de funciones comunes del servicio de entrada de TV. Está diseñado para que lo usen los OEMs para crear solo para Android 5.0 (nivel de API 21) hasta Android 7.1 (nivel de API 25).

Cómo actualizar tu proyecto

La Biblioteca complementaria del TIF está disponible para que los OEM la usen de forma heredada en la androidtv-sample-inputs en un repositorio de confianza. Consulta ese repositorio para ver un ejemplo de cómo incluir la biblioteca en una app.

Cómo declarar tu servicio de entrada de TV en el manifiesto

Tu app debe proporcionar un permiso compatible con TvInputService que el sistema usa para acceder a tu app. El TIF La Biblioteca complementaria proporciona la clase BaseTvInputService, que proporciona una implementación predeterminada de TvInputService que puedes personalizar. Crea una subclase de BaseTvInputService. y declarar la subclase en tu manifiesto como servicio.

Dentro de la declaración del manifiesto, especifica el BIND_TV_INPUT para permitir para conectar la entrada de TV al sistema. Un servicio del sistema realiza la vinculación y tiene la BIND_TV_INPUT. La app de TV del sistema envía solicitudes a los servicios de entrada de TV. a través de la interfaz TvInputManager

En tu declaración de servicio, incluye un filtro de intents que especifique TvInputService como la acción que se debe realizar con el . También declara los metadatos del servicio como recurso XML independiente. El Se muestran la declaración del servicio, el filtro de intents y la declaración de metadatos del servicio. en el siguiente ejemplo:

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

Define los metadatos del servicio en un archivo XML independiente. El servicio El archivo en formato XML de metadatos debe incluir una interfaz de configuración que describa el funcionamiento de configuración inicial y análisis de canales. El archivo de metadatos también debe contener una marca que indica si los usuarios pueden grabar contenido o no. Para ver más información sobre cómo admitir la grabación de contenido en tu app, consulta Admite la grabación de contenido.

El archivo de metadatos del servicio se encuentra en el directorio de recursos XML. para tu app y debe coincidir con el nombre del recurso que declaraste en el . Con las entradas del manifiesto del ejemplo anterior, deberías crea el archivo en formato XML en res/xml/richtvinputservice.xml, con el el siguiente contenido:

<?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" />

Cómo definir canales y crear tu actividad de configuración

El servicio de entrada de TV debe definir al menos un canal que los usuarios a través de la app del sistema para TV. Deberías registrar tus canales en la base de datos del sistema y proporcionan una actividad de configuración invoca cuando no puede encontrar un canal para tu app.

Primero, habilita tu app para leer y escribir en el sistema electrónico. Guía de programación (EPG), cuyos datos incluyen los canales y programas disponibles para el usuario. Para permitir que tu app realice estas acciones y realizar la sincronización con la EPG después de reiniciar el dispositivo, agrega los siguientes elementos al manifiesto de la app:

<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED "/>

Agrega el siguiente elemento para asegurarte de que tu app aparezca en el Google Play Store como una app que proporciona canales de contenido en Android TV:

<uses-feature
    android:name="android.software.live_tv"
    android:required="true" />

A continuación, crea una clase que extienda EpgSyncJobService . Esta clase abstracta facilita la creación de un servicio de trabajo que crea y actualiza canales en la base de datos del sistema.

En la subclase, crea y muestra la lista completa de canales en getChannels() Si tus canales provienen de un archivo XMLTV, usa la clase XmlTvParser. De lo contrario, genera canales de manera programática con la clase Channel.Builder.

Para cada canal, el sistema llama a getProgramsForChannel(). cuando se necesita una lista de programas que se puedan ver en un período determinado en el canal. Muestra una lista de objetos Program para el canal. Usa la clase XmlTvParser para obtener programas de un XMLTV o puedes generarlos de forma programática con el Clase Program.Builder.

Para cada objeto Program, usa un InternalProviderData para configurar información del programa, como la el tipo de video del programa. Si solo tienes una cantidad limitada de programas si quieres que el canal se repita indefinidamente, Método InternalProviderData.setRepeatable() con un valor de true cuando configures la información del programa.

Después de implementar el servicio de trabajo, agrégalo al manifiesto de tu app:

<service
    android:name=".sync.SampleJobService"
    android:permission="android.permission.BIND_JOB_SERVICE"
    android:exported="true" />

Finalmente, crea una actividad de configuración. Tu actividad de configuración debe proporcionar una manera para sincronizar los datos del canal y del programa. Una forma de hacerlo es que el usuario a través de la IU en la actividad. También puedes hacer que la app lo haga automáticamente cuando comienza la actividad. Cuando la actividad de configuración necesita sincronizar los canales y la información del programa, la app debería iniciar el servicio de trabajo:

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));

Usa el método requestImmediateSync() para sincronizar el servicio de trabajo. El usuario debe esperar a que finalice la sincronización, por lo que mantén tu período de solicitud relativamente corto.

Usa el método setUpPeriodicSync() para que el servicio de trabajo Sincroniza periódicamente los datos de los canales y los programas en segundo plano:

Kotlin

EpgSyncJobService.setUpPeriodicSync(
        context,
        inputId,
        ComponentName(context, SampleJobService::class.java)
)

Java

EpgSyncJobService.setUpPeriodicSync(context, inputId,
        new ComponentName(context, SampleJobService.class));

La Biblioteca complementaria del TIF proporciona un método adicional sobrecargado de requestImmediateSync(), que te permite especificar la duración de que los datos del canal se sincronicen en milisegundos. El método predeterminado sincroniza de datos del canal.

La Biblioteca complementaria del TIF proporciona un método adicional de sobrecarga setUpPeriodicSync(), que te permite especificar la duración de los datos de cada canal y la frecuencia con la que se debe realizar la sincronización periódica. El El método predeterminado sincroniza 48 horas de datos del canal cada 12 horas.

Para obtener más detalles sobre los datos del canal y la EPG, consulta Trabaja con datos del canal.

Cómo manejar solicitudes de sintonización y reproducción de contenido multimedia

Cuando un usuario selecciona un canal específico, la app de TV del sistema usa un Session, creado por tu app, para sintonizar el canal solicitado y reproducir contenido. La Biblioteca complementaria del TIF ofrece que puedes extender para manejar llamadas de canal y sesión desde el sistema.

La subclase BaseTvInputService crea sesiones que controlan ajustar las solicitudes. Anula el onCreateSession(), crea una sesión extendida de la clase BaseTvInputService.Session y llama a super.sessionCreated() con tu nueva sesión. En la siguiente ejemplo, onCreateSession() muestra un Un objeto RichTvInputSessionImpl que extiende 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;
}

Cuando el usuario utiliza la app de TV del sistema para comenzar a ver uno de tus canales, el sistema llama al método onPlayChannel() de tu sesión. Anular este método si necesitas realizar una inicialización especial de canales antes de comienza a reproducirse el programa.

Luego, el sistema obtiene el programa programado en ese momento y llama a tu método onPlayProgram() de la sesión, que especifica el programa información y el tiempo de inicio en milisegundos. Usa el TvPlayer para comenzar a reproducir el programa.

El código de tu reproductor multimedia debería implementar TvPlayer para controlar. eventos de reproducción específicos. La clase TvPlayer controla las funciones como controles de pausa en directo sin agregar complejidad a tu Implementación de BaseTvInputService.

En el método getTvPlayer() de tu sesión, muestra el reproductor multimedia que implementa TvPlayer. El La app de ejemplo de servicio de entrada de TV implementa un reproductor multimedia que usa ExoPlayer.

Cómo crear un servicio de entrada de TV con el marco de trabajo de entrada de TV

Si tu servicio de entrada de TV no puede usar la Biblioteca complementaria del TIF, debes hacer lo siguiente: para implementar los siguientes componentes:

  • TvInputService proporciona disponibilidad de larga duración y en segundo plano para la entrada de TV
  • TvInputService.Session mantiene el estado de la entrada de TV y se comunica. con la app de hosting
  • TvContract, que describe los canales y programas disponibles para la TV. entrada
  • TvContract.Channels, que representa información acerca de un canal de TV.
  • TvContract.Programs describe un programa de TV con datos, como el programa. título y hora de inicio
  • TvTrackInfo, que representa una pista de audio, video o subtítulo.
  • TvContentRating describe una clasificación de contenido y permite contenido personalizado. esquemas de calificación
  • TvInputManager proporciona una API a la app de TV del sistema y administra. la interacción con entradas de TV y apps

También debes realizar lo siguiente:

  1. Declara tu servicio de entrada de TV en el manifiesto, como se muestra a continuación: que se describe en Cómo declarar tu servicio de entrada de TV en la .
  2. Crea el archivo de metadatos del servicio.
  3. Crea y registra la información del canal y el programa.
  4. Crea la actividad de configuración.

Cómo definir tu servicio de entrada de TV

Para tu servicio, extiende la clase TvInputService. R La implementación de TvInputService es una servicio vinculado donde el servicio del sistema es el cliente que se vincula a él. Los métodos del ciclo de vida del servicio que debes implementar, se ilustran en la figura 1.

El método onCreate() inicializa e inicia la HandlerThread, que proporciona un subproceso independiente del subproceso de IU para gestionar acciones impulsadas por el sistema. En el siguiente ejemplo, onCreate() inicializa el objeto CaptioningManager y se prepara para controlar ACTION_BLOCKED_RATINGS_CHANGED y ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED. Estos acciones describen los intents del sistema que se activan cuando el usuario cambia la configuración de los controles parentales se realizó un cambio en la lista de clasificaciones bloqueadas.

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);
}

Figura 1: Ciclo de vida de TvInputService

Consulta Controla el contenido para obtener más información sobre cómo trabajar con contenido bloqueado y proporcionar con los controles parentales. Consulta TvInputManager para ver más acciones impulsadas por el sistema que que recomendamos en tu servicio de entrada de TV.

TvInputService crea un TvInputService.Session que implementa Handler.Callback para manejar los cambios de estado del reproductor. Con onSetSurface(), TvInputService.Session establece Surface con el contenido de video. Consulta Cómo integrar el jugador con la plataforma y obtén más información para trabajar con Surface en la renderización de videos.

TvInputService.Session controla las onTune() cuando el usuario selecciona un canal, y notifica a la app de TV del sistema cuando hay cambios en el contenido y metadatos de contenido. Estos métodos notify() se describen en Cómo controlar el contenido y Cómo controlar la selección de pistas más adelante en esta capacitación.

Cómo definir la actividad de configuración

La app de TV del sistema funciona con la actividad de configuración que defines para tu entrada de TV. El la actividad de configuración es obligatoria y debe proporcionar al menos un registro de canal para la base de datos del sistema. El la app de TV del sistema invoca la actividad de configuración cuando no puede encontrar un canal para la entrada de TV.

La actividad de configuración describe a la app de TV del sistema los canales disponibles a través de la TV. entrada, como se demuestra en la siguiente lección, Crear y actualizar los datos de los canales.

Referencias adicionales