Direct surface rendering lets your app control what is rendered and how it is rendered. You can overlay metadata such as channel attribution. You can also render secured content for protected playback.
To implement direct rendering, you must build your own TvInputService
.
The Android TV home screen passes a Surface
to your service by calling
onSetSurface()
.
Your app draws video directly on this surface from onTune()
.
This page explains how to build a TvInputService
and configure your app to render
preview material by itself, rather than relying on the Android TV home screen.
Declare your TvInputService in the manifest
Your app must provide a TvInputService
-compatible
service that the system uses to access your app.
In your service declaration, include an intent filter that specifies
TvInputService
as the action to perform with the
intent. Also declare the service metadata as a separate XML resource. The
service declaration, intent filter, and service metadata declaration are shown
in the following example:
<service android:name=".rich.PreviewInputService" 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 PreviewInputSetupActivity to the system/TV app. --> <meta-data android:name="android.media.tv.input" android:resource="@xml/previewinputservice" /> </service>
Define the service metadata in a separate XML file.
The service metadata file is located in the XML resources directory
for your app and must match the name of the resource you declared in the
manifest. Using the manifest entries from the previous example, you would
create the XML file at res/xml/previewinputservice.xml
, with the
following contents:
<?xml version="1.0" encoding="utf-8"?>
<tv-input/>
Create a video URI
To indicate that your preview video should be rendered by your app, rather than
the Android TV home screen, create a video URI for a PreviewProgram
:
componentName = new ComponentName(context, PreviewInputService.class.getName());
Uri videoUri = TvContractCompat.buildPreviewProgramUri(previewProgramId)
.buildUpon()
.appendQueryParameter("input", TvContractCompat.buildInputId(componentName));
Use the videoUri
to
update a preview program
of a launcher channel. Specify the videoUri in the
PreviewProgram.Builder
with setPreviewVideoUri
when updating a preview program:
PreviewProgram.Builder builder = new PreviewProgram.Builder(previewProgram);
builder.setChannelId(channelId)
.setType(TvContractCompat.PreviewPrograms.TYPE_CLIP)
.setTitle("Title")
.setDescription("Program description")
.setPosterArtUri(uri)
.setIntentUri(uri)
.setInternalProviderId(appProgramId)
.setPreviewVideoUri(videoUri);
context.getContentResolver().update(
TvContractCompat.buildPreviewProgramUri(programId),
previewProgramBuilder.build().toContentValues(),
null,
null);
Your app calls
onTune(Uri videoUri)
to make Android TV start the preview video.
Create a service
The following example shows how to extend TvInputService
to create your own
PreviewInputService
. Note that the service uses a MediaPlayer
for playback,
but your code can use any available video player.
import android.content.Context;
import android.media.MediaPlayer;
import android.media.tv.TvInputService;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.Surface;
import java.io.IOException;
public class PreviewInputService extends TvInputService {
@Nullable
@Override
public Session onCreateSession(String s) {
Log.d("PreviewInputService", s);
return new PreviewSession(this);
}
private class PreviewSession extends Session {
MediaPlayer mMediaPlayer = null;
Context mContext;
public PreviewSession(Context context) {
super(context);
mContext = context;
mMediaPlayer = new MediaPlayer();
}
@Override
public void onRelease() {
if(mMediaPlayer != null) {
mMediaPlayer.release();
mMediaPlayer = null;
}
}
@Override
public boolean onTune(Uri uri) {
// fetch the stream url from the TV Provider database
// for content://android.media.tv/preview_program/14
Uri videoUri = fetchStreamUrlFromTvProviderDatabase(uri);
try {
mMediaPlayer.setDataSource(videoUri.toString());
mMediaPlayer.prepare();
mMediaPlayer.start();
notifyVideoAvailable();
} catch (IOException e) {
Log.e("PreviewInputService", "Could not prepare media player", e);
return false;
}
return true;
}
@Override
public boolean onSetSurface(@Nullable Surface surface) {
mMediaPlayer.setSurface(surface);
return true;
}
@Override
public void onSetStreamVolume(float v) {
// set stream volume here
}
@Override
public void onSetCaptionEnabled(boolean b) {
// enable/disable captions here
}
}
}