Beim Live-TV wechselt der Nutzer den Kanal und es wird eine Kanal- und Programminformationen kurz, bevor die Informationen verschwinden. Andere Arten von Informationen, wie z. B. Nachrichten ("DO NOT ATTEMPT AT HOME"), Untertitel oder Werbung. Wie bei jedem Fernseher App dürfen diese Informationen die auf dem Bildschirm wiedergegebenen Programminhalte nicht beeinträchtigen.
Überlegen Sie auch, ob bestimmte Programminhalte angesichts des die Altersfreigabe und die Jugendschutzeinstellungen sowie das Verhalten Ihrer App und Informationen Inhalte blockiert sind oder nicht verfügbar sind. In dieser Lektion wird beschrieben, wie Sie den Nutzer Erfahrung für diese Überlegungen.
Probieren Sie die aus. Beispiel-App „TV Input Service“
Player in die Oberfläche integrieren
Deine TV-Eingabe muss das Video in einem Surface
-Objekt rendern, das über
die TvInputService.Session.onSetSurface()
. Hier ist ein Beispiel, wie eine MediaPlayer
-Instanz zum Spielen verwendet wird.
Inhalt im Surface
-Objekt:
Kotlin
override fun onSetSurface(surface: Surface?): Boolean { player?.setSurface(surface) mSurface = surface return true } override fun onSetStreamVolume(volume: Float) { player?.setVolume(volume, volume) mVolume = volume }
Java
@Override public boolean onSetSurface(Surface surface) { if (player != null) { player.setSurface(surface); } mSurface = surface; return true; } @Override public void onSetStreamVolume(float volume) { if (player != null) { player.setVolume(volume, volume); } mVolume = volume; }
Mit ExoPlayer:
Kotlin
override fun onSetSurface(surface: Surface?): Boolean { player?.createMessage(videoRenderer)?.apply { type = MSG_SET_SURFACE payload = surface send() } mSurface = surface return true } override fun onSetStreamVolume(volume: Float) { player?.createMessage(audioRenderer)?.apply { type = MSG_SET_VOLUME payload = volume send() } mVolume = volume }
Java
@Override public boolean onSetSurface(@Nullable Surface surface) { if (player != null) { player.createMessage(videoRenderer) .setType(MSG_SET_SURFACE) .setPayload(surface) .send(); } mSurface = surface; return true; } @Override public void onSetStreamVolume(float volume) { if (player != null) { player.createMessage(videoRenderer) .setType(MSG_SET_VOLUME) .setPayload(volume) .send(); } mVolume = volume; }
Overlay verwenden
Mit einem Overlay können Sie Untertitel, Nachrichten, Werbung oder MHEG-5-Datensendungen einblenden. Standardmäßig enthält der Parameter
Overlay ist deaktiviert. Sie können sie beim Erstellen der Sitzung durch folgenden Aufruf aktivieren:
TvInputService.Session.setOverlayViewEnabled(true)
,
wie im folgenden Beispiel:
Kotlin
override fun onCreateSession(inputId: String): Session = onCreateSessionInternal(inputId).apply { setOverlayViewEnabled(true) sessions.add(this) }
Java
@Override public final Session onCreateSession(String inputId) { BaseTvInputSessionImpl session = onCreateSessionInternal(inputId); session.setOverlayViewEnabled(true); sessions.add(session); return session; }
Verwenden Sie für das Overlay ein View
-Objekt, das wie hier gezeigt von TvInputService.Session.onCreateOverlayView()
zurückgegeben wird:
Kotlin
override fun onCreateOverlayView(): View = (context.getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater).run { inflate(R.layout.overlayview, null).apply { subtitleView = findViewById<SubtitleView>(R.id.subtitles).apply { // Configure the subtitle view. val captionStyle: CaptionStyleCompat = CaptionStyleCompat.createFromCaptionStyle(captioningManager.userStyle) setStyle(captionStyle) setFractionalTextSize(captioningManager.fontScale) } } }
Java
@Override public View onCreateOverlayView() { LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.overlayview, null); subtitleView = (SubtitleView) view.findViewById(R.id.subtitles); // Configure the subtitle view. CaptionStyleCompat captionStyle; captionStyle = CaptionStyleCompat.createFromCaptionStyle( captioningManager.getUserStyle()); subtitleView.setStyle(captionStyle); subtitleView.setFractionalTextSize(captioningManager.fontScale); return view; }
Die Layout-Definition für das Overlay könnte wie folgt aussehen:
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.exoplayer.text.SubtitleView android:id="@+id/subtitles" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|center_horizontal" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" android:layout_marginBottom="32dp" android:visibility="invisible"/> </FrameLayout>
Inhalt steuern
Wenn der Nutzer einen Kanal auswählt, verarbeitet der TV-Eingang den onTune()
-Callback im TvInputService.Session
-Objekt. Der Systemfernseher
Die Jugendschutzeinstellungen der App bestimmen, welche Inhalte basierend auf der Altersfreigabe angezeigt werden.
In den folgenden Abschnitten wird beschrieben, wie du die Kanal- und Programmauswahl mithilfe der
TvInputService.Session
notify
-Methoden, die
mit der TV-App des Systems kommunizieren.
Video nicht mehr verfügbar machen
Wenn der Nutzer den Kanal wechselt, möchten Sie sicherstellen, dass auf dem Bildschirm keine
Videoartefakte, bevor die Inhalte auf dem Fernseher gerendert werden. Wenn Sie TvInputService.Session.onTune()
anrufen,
kannst du verhindern, dass das Video präsentiert wird, indem du TvInputService.Session.notifyVideoUnavailable()
aufrufst.
und die Konstante VIDEO_UNAVAILABLE_REASON_TUNING
übergeben,
wie im folgenden Beispiel dargestellt.
Kotlin
override fun onTune(channelUri: Uri): Boolean { subtitleView?.visibility = View.INVISIBLE notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING) unblockedRatingSet.clear() dbHandler.apply { removeCallbacks(playCurrentProgramRunnable) playCurrentProgramRunnable = PlayCurrentProgramRunnable(channelUri) post(playCurrentProgramRunnable) } return true }
Java
@Override public boolean onTune(Uri channelUri) { if (subtitleView != null) { subtitleView.setVisibility(View.INVISIBLE); } notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING); unblockedRatingSet.clear(); dbHandler.removeCallbacks(playCurrentProgramRunnable); playCurrentProgramRunnable = new PlayCurrentProgramRunnable(channelUri); dbHandler.post(playCurrentProgramRunnable); return true; }
Wenn der Inhalt dann im Surface
gerendert wird, rufen Sie
TvInputService.Session.notifyVideoAvailable()
um das Video anzuzeigen. Beispiel:
Kotlin
fun onRenderedFirstFrame(surface:Surface) { firstFrameDrawn = true notifyVideoAvailable() }
Java
@Override public void onRenderedFirstFrame(Surface surface) { firstFrameDrawn = true; notifyVideoAvailable(); }
Dieser Übergang dauert nur den Bruchteil einer Sekunde. visuell besser, als seltsame Bildblasen zu blinken.
Weitere Informationen zur Arbeit findest du unter Player in Oberfläche integrieren.
mit Surface
, um das Video zu rendern.
Jugendschutzeinstellungen anbieten
Um herauszufinden, ob bestimmte Inhalte durch die Jugendschutzeinstellungen und die Altersfreigabe blockiert sind, sehen Sie sich das
TvInputManager
Klassenmethoden, isParentalControlsEnabled()
und isRatingBlocked(android.media.tv.TvContentRating)
. Ich
Sie sollten auch sicherstellen, dass das TvContentRating
des Inhalts in einem
der aktuell zulässigen Altersfreigaben. Diese Überlegungen werden im folgenden Beispiel gezeigt.
Kotlin
private fun checkContentBlockNeeded() { currentContentRating?.also { rating -> if (!tvInputManager.isParentalControlsEnabled || !tvInputManager.isRatingBlocked(rating) || unblockedRatingSet.contains(rating)) { // Content rating is changed so we don't need to block anymore. // Unblock content here explicitly to resume playback. unblockContent(null) return } } lastBlockedRating = currentContentRating player?.run { // Children restricted content might be blocked by TV app as well, // but TIF should do its best not to show any single frame of blocked content. releasePlayer() } notifyContentBlocked(currentContentRating) }
Java
private void checkContentBlockNeeded() { if (currentContentRating == null || !tvInputManager.isParentalControlsEnabled() || !tvInputManager.isRatingBlocked(currentContentRating) || unblockedRatingSet.contains(currentContentRating)) { // Content rating is changed so we don't need to block anymore. // Unblock content here explicitly to resume playback. unblockContent(null); return; } lastBlockedRating = currentContentRating; if (player != null) { // Children restricted content might be blocked by TV app as well, // but TIF should do its best not to show any single frame of blocked content. releasePlayer(); } notifyContentBlocked(currentContentRating); }
Benachrichtigen Sie den Systemfernseher, nachdem Sie festgestellt haben, ob die Inhalte blockiert werden sollen oder nicht.
indem Sie die Methode
TvInputService.Session
-Methode notifyContentAllowed()
oder
notifyContentBlocked()
aus, wie im vorherigen Beispiel gezeigt.
Verwenden Sie die Klasse TvContentRating
, um den systemdefinierten String für
den COLUMN_CONTENT_RATING
durch den
TvContentRating.createRating()
wie hier gezeigt:
Kotlin
val rating = TvContentRating.createRating( "com.android.tv", "US_TV", "US_TV_PG", "US_TV_D", "US_TV_L" )
Java
TvContentRating rating = TvContentRating.createRating( "com.android.tv", "US_TV", "US_TV_PG", "US_TV_D", "US_TV_L");
Titelauswahl verwalten
Die Klasse TvTrackInfo
enthält Informationen zu Medien-Tracks wie
als Tracktyp (Video, Audio oder Untertitel) usw.
Wenn Ihre TV-Eingabesitzung zum ersten Mal Titelinformationen abrufen kann, sollte Folgendes aufgerufen werden:
TvInputService.Session.notifyTracksChanged()
durch eine Liste aller Titel zum Aktualisieren der System-TV-App. Wenn ich dort
eine Änderung der Titelinformationen,
notifyTracksChanged()
um das System zu aktualisieren.
Die System-TV-App bietet eine Oberfläche, über die Nutzer einen bestimmten Titel auswählen können, falls mehrere
Titel für einen bestimmten Track-Typ verfügbar ist, zum Beispiel Untertitel in
verschiedenen Sprachen. Mein Fernseher
die Eingabe auf den
onSelectTrack()
über die System-TV-App aufrufen, indem Sie
notifyTrackSelected()
aus, wie im folgenden Beispiel gezeigt. Hinweis: Wenn null
als Track-ID übergeben wird, wird die Auswahl für den Track aufgehoben.
Kotlin
override fun onSelectTrack(type: Int, trackId: String?): Boolean = mPlayer?.let { player -> if (type == TvTrackInfo.TYPE_SUBTITLE) { if (!captionEnabled && trackId != null) return false selectedSubtitleTrackId = trackId subtitleView.visibility = if (trackId == null) View.INVISIBLE else View.VISIBLE } player.trackInfo.indexOfFirst { it.trackType == type }.let { trackIndex -> if( trackIndex >= 0) { player.selectTrack(trackIndex) notifyTrackSelected(type, trackId) true } else false } } ?: false
Java
@Override public boolean onSelectTrack(int type, String trackId) { if (player != null) { if (type == TvTrackInfo.TYPE_SUBTITLE) { if (!captionEnabled && trackId != null) { return false; } selectedSubtitleTrackId = trackId; if (trackId == null) { subtitleView.setVisibility(View.INVISIBLE); } } int trackIndex = -1; MediaPlayer.TrackInfo[] trackInfos = player.getTrackInfo(); for (int index = 0; index < trackInfos.length; index++) { MediaPlayer.TrackInfo trackInfo = trackInfos[index]; if (trackInfo.getTrackType() == type) { trackIndex = index; break; } } if (trackIndex >= 0) { player.selectTrack(trackIndex); notifyTrackSelected(type, trackId); return true; } } return false; }