Skip to main content
Skip table of contents

Android TV preview

To test this feature and view the example code, please see the Android SDK 5 Example Code Quick Start guide.

This feature enables previews of unencrypted and encrypted channels on the Android TV home screen. This example is derived from a Google codelabs project highlighting how to use the SDK to preview streams.

For simplicity, this guide focuses on the elements related to the SDK. Some background knowledge on how apps can be integrated with the Android TV system may help; see https://developer.android.com/training/tv.

Prerequisites

The following assumes knowledge of setting up and maintaining application channels and programs.

You should also follow the Google doc for rendering previews on a surface.

Using the SDK

All use of the SDK to preview should take place in an instance of a subclass of TvInputService - see the implementation of PreviewInputService in the example for reference. As previewing is carried out by a service rather than the app itself, care must be taken to load the SDK in the service before attempting to preview.

JAVA
private static class PreviewSession extends Session {
    PreviewSession(Context context) {
        super(context);
        mContext = context;
        OTVSDK.load(context);
    …
}

Using OTVVideoView to preview

  1. After loading the SDK, the previewing service needs to construct an instance of OTVVideoView.

    JAVA
    private static class PreviewSession extends Session {
        PreviewSession(Context context) {
            super(context);
            mContext = context;
            OTVSDK.load(context);
            mVideoView = new OTVVideoView(mContext);
        }
        …
    }

    If the streaming content is served over HTTPS, under certain circumstances you may need to disable SSL verification.

  2. Attach the system-provided surface to the OTVVideoView object in the session's onSetSurface() callback. The SDK has an OTVVideoView::setAndroidTVPreviewSurface() API specifically for use in rendering the previews.

    JAVA
    @Override
    public boolean onSetSurface(@Nullable Surface surface) {
        if (mVideoView != null) {
            mVideoView.setAndroidTVPreviewSurface(surface);
        }
        mSurface = surface;
        return true;
    }

    The previewing service must maintain the surface lifecycle and clear the surface once destroyed.

  3. The previewing service determines the stream (and any necessary parameters) from the URI given in the onTune() callback, and starts the stream in a background thread after setting the preview URL to mVideoView.setVideoPath(previewUrl).

    JAVA
    @Override
    public boolean onTune(Uri channelUri) {
        // Let the TvInputService know that the video is being loaded.
        notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
    
        // Load your video in the background.
        BackgroundPlayback playbackThread = new BackgroundPlayback(channelUri);
        playbackThread.start();
    
        return true;
    }
    
    class BackgroundPlayback extends Thread {
        Uri innerUri;
        private static final String DRM_URI = "https://drm.example.com/";
        private static final Map<String, String> DRM_HEADERS = new HashMap<>();
        static {
            // Server headers
            DRM_HEADERS.put("Accept", "application/octet-stream");
            DRM_HEADERS.put("Content-Type", "application/octet-stream");
        }
    
        public BackgroundPlayback(Uri channelUri ) {
            innerUri = channelUri;
        }
    
        @Override
        public void run(){
            if (innerUri == null) {
                notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN);
                return;
            }
    
            try {
                String videoPath = innerUri.getQueryParameter("previewVideoUri");
                String token = innerUri.getQueryParameter("token");
                if(token != null) { //ie. the stream is encrypted
                    // this example shows streams with customised encryption 
                    mDrmCallback = new OTVHttpMediaDrmCallback(DRM_URI);
                    for (Map.Entry<String, String> pair : DRM_HEADERS.entrySet()) {
                        mDrmCallback.setKeyRequestProperty(pair.getKey(), pair.getValue());
                    }
                    mDrmCallback.setKeyRequestProperty("nv-authorizations", token);
                    mVideoView.setMediaDrmCallback(mDrmCallback);
                } else {
                    mVideoView.setMediaDrmCallback(null);
                }
                mVideoView.setAndroidTVPreviewSurface(mSurface);
                mVideoView.setVideoPath(videoPath);
                mVideoView.start();
                notifyVideoAvailable();
            } catch (Exception e) {
                Log.e(TAG, "Could not prepare media player", e);
                notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN);
            }
        }
    }
  4. On stopping the playback, the service needs to set the OTVVideoView  surface to null in the onRelease() callback. Here the player is destroyed as the playback is stopped for the preview ending the player's lifecycle.

    JAVA
    @Override
    public void onRelease() {
        if (mVideoView != null) {
            mVideoView.stopPlayback();
            mVideoView.setAndroidTVPreviewSurface(null);
        }
        mVideoView = null;
        Log.i(TAG, "onRelease leave");
    }
JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.