Skip to content

Most visited

Recently visited

navigation

Render Video Directly on a Surface

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
       }
   }
}
This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Follow Google Developers on WeChat

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience.
(Sep 2017 survey)