Stream Transfer

The Android TV Receiver SDK provides the ability to transfer a stream into and from your native Android TV Receiver app. Note that stream transferring into your native app will not work if your app has a LaunchRequestChecker that checks whether credentials data is valid, because the CredentialsData is empty in the launch request.

Transferring into native Android TV Receiver apps

Similar to load requests, stream transfer requests are passed to your app via intents.

You should first add the following intent filter to your AndroidManifest.xml:

<activity android:name="com.example.PlayerActivity">
  <intent-filter>
     <action android:name="com.google.android.gms.cast.tv.action.RESUME_SESSION"/>
     <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
</activity>

If you are calling MediaManager#onNewIntent(Intent) for load intents already, you don’t need to do anything additional to forward stream transfer intents to MediaManager.

Next, override MediaCommandCallback#onResumeSession() to handle the load request:

public MyMediaCommandCallback extends MediaLoadCommandCallback {
  @Override
  public Task<MediaLoadRequestData> onResumeSession(
      String senderId, MediaResumeSessionRequestData mediaResumeSessionRequestData) {
    return Tasks.call(() -> {
        // Extract the load request data from the request.
        MediaLoadRequestData loadRequestData =
            mediaResumeSessionRequestData.getSessionState().getLoadRequestData();

        // Resolve the entity into your data structure and load media.
        MediaInfo mediaInfo = loadRequestData.getMediaInfo();

        // Fill in any missing parts of the mediaInfo.
        myFillMediaInfo(new MediaInfoWriter(mediaInfo));

        // Use the mediaInfo to load the content with your player.
        myPlayerLoad(mediaInfo.getContentUrl());

        // Update media metadata and state (this clears all previous status
        // overrides).
        castReceiverContext.getMediaManager()
            .setDataFromLoad(loadRequestData);
        …

        // Broadcast an updated media status.
        castReceiverContext.getMediaManager().broadcastMediaStatus();

        // Return the resolved MediaLoadRequestData to indicate load success.
        return loadRequestData;
    });
}

Transferring out from Android TV Receiver apps

To transfer from your Android TV Receiver app to another device, declare MediaStatus#COMMAND_STREAM_TRANSFER in your supported media commands and handle the MediaCommandCallback#onStoreSession() method:

// Declare outward stream transfer is supported.
CastReceiverContext
    .getInstance()
    .getMediaManager()
    .getMediaStatusModifier()
    .setMediaCommandSupported(MediaStatus.COMMAND_STREAM_TRANSFER, true);

For the MediaCommandCallback#onStoreSession(), there are three options:

1. Do not provide any implementation and the SDK will default to providing the current SessionState from the MediaStatus.

2. Use the default SessionState generated by the SDK using the MediaStatus to create a new StoreSessionResponseData.

// Handle storing the session state for stream transfer.
public class MyMediaCommandCallback extends MediaCommandCallback {
  /** Callback for storing the session state. */
  @Override
  public Task<StoreSessionResponseData> onStoreSession(
    String senderId, int type, StoreSessionRequestData requestData) {
      return super
          .onStoreSession(senderId, requestData)
          .continueWith((responseData)
              -> myProcessStoreSessionResponseData(responseData));
  }
}

private StoreSessionResponseData myProcessStoreSessionResponseData(
    Task<StoreSessionResponseData> defaultResponseTask) throws Exception {
  if (!defaultResponseTask.isSuccessful()) {
    throw defaultResponseTask.getException();
  }

  StoreSessionResponseData ssrd = defaultResponseTask.getResult();
  SessionState originalSessionState = ssrd.getSessionState();
  MediaLoadRequestData originaLoadRequestData =
    originalSessionState.getLoadRequestData();

  // Copy originalLoadRequestData and update fields as needed.
  MediaLoadRequestData mediaLoadRequestData =
       new MediaLoadRequestData.Builder(originaLoadRequestData)
               ...
               .build();

  SessionState sessionState = new SessionState.Builder()
               .setLoadRequestData(mediaLoadRequestData)
               .setCustomData(originalSessionState.getCustomData())
               .build();

  StoreSessionResponseData responseDataBuilder =
       new StoreSessionResponseData.Builder()
              .setSessionState(sessionState)
              .setCustomData(ssrd.getCustomData())
              .build();

  return responseDataBuilder.build();
}

3. Generate your own SessionState from scratch:

// Handle storing the session state for stream transfer.
public class MyMediaCommandCallback extends MediaCommandCallback {
  /** Callback for storing the session state. */
  @Override
  public Task<StoreSessionResponseData> onStoreSession(
    String senderId, int type, StoreSessionRequestData requestData) {

    SessionState sessionState = myGenerateSessionState();
    return Tasks.forResult(
        new StoreSessionResponseData.Builder()
            .setSessionState(sessionState)
            .build());
  }
}

private SessionState myGenerateSessionState() {
   MediaLoadRequestData mediaLoadRequestData =
           new MediaLoadRequestData.Builder()
              .setMediaInfo(mMediaManager.getCurrentMediaStatus().getMediaInfo())
              .setCurrentTime
                        (mMediaManager.getCurrentMediaStatus().getStreamPosition())
              .setCustomData(mCustomData)
              .setAutoplay(true)
              .build();
   SessionState sessionState = new SessionState.Builder()
              .setLoadRequestData(mediaLoadRequestData)
              .build();
   return sessionState;
}

The SessionState class contains:

  • A MediaLoadRequestData field, indicating the current media status, including the media, the current playback state, and the queue.
  • An optional customData field, where you can put additional information into a JSONObject.

The SessionState class should be encapsulated into a StoreSessionResponseData object, and it will be sent to the transferee in a ResumeSessionRequestData.