11

Using the Google CAF Receiver SDK, how do we prevent the receiver from timing out and automatically killing the cast session when we're not using the receiver player?

The standard Google Cast use case is to send media from a device to the cast receiver and have the receiver render the media using a player. The CAF receiver SDK provides this functionality in a beautiful, simple way using the element cast-media-player.

But for those instances when we want to cast from a device and render content where it's not relevant to use the cast-media-player (e.g. an HTML dashboard), how do we keep the receiver alive?

The following custom receiver for example (HAML for brevity), results in the cast session automatically terminating after 5 minutes...

!!! 5
%html
  %head
    :css
      cast-media-player {
        display: none;
      }

    = javascript_include_tag 'https://www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js'
  %body
    %cast-media-player

    :javascript
      const context = cast.framework.CastReceiverContext.getInstance();
      const player = context.getPlayerManager();

      player.setMessageInterceptor(cast.framework.messages.MessageType.LOAD, loadRequestData => {
        ...[load custom view]...
        return false;
      });

      context.start();

The receiver log shows the line cast.framework.common.IdleTimeoutManager] timer expired and then shuts down. Example receiver log shown here.

I've tried:

  • Increasing cast.framework.CastReceiverOptions#maxInactivity to a very large number
  • Periodically loading new data from the sender
  • Periodically sending custom messages from the receiver to the sender
  • Periodically sending custom messages from the sender to the receiver

Any help is very much appreciated!

Elliott
  • 4,598
  • 1
  • 24
  • 39

4 Answers4

7

I ran into the same problem while developing a custom receiver app that does not play media. Here is the solution I implemented:

var idleTime = 0;

const context = cast.framework.CastReceiverContext.getInstance();

const CUSTOM_CHANNEL = '[MY CHANNEL HERE]';

context.addCustomMessageListener(CUSTOM_CHANNEL, function(customEvent) {
    var eventData = customEvent.data;
    parseCommand(eventData);
    idleTime = 0;
});


const options = new cast.framework.CastReceiverOptions();

options.disableIdleTimeout = true;

context.start(options);

var idleInterval = setInterval(timerIncrement, 60000); // 1 minute

function timerIncrement() {
    idleTime = idleTime + 1;
    if (idleTime > 4) { // 5 minutes
        context.stop();
    }
}

With CastReveiverOptions I disable idle timeout, which according to the documentation: "If true, the receiver will not set an idle timeout to close receiver if there is no activity. Should only be used for non media apps." https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.CastReceiverOptions#constructor_1

Since mine is a "non media app," I believe this is correct usage. I then set my own time out based on 5 minutes of inactivity in my custom channel.

Sasha Tsukanov
  • 1,025
  • 9
  • 20
JRP
  • 83
  • 1
  • 8
5

I figured out an alternative way to stop this which is more efficient than periodically sending a silent clip, but it feels dirty. Basically we have to stop Chromecast's setTimeout from firing and closing the connection due to no media. The quickest solution is to simply re-declare setTimeout as a dummy no-op function before loading the Chromecast receiver script. It does not seem to break anything Chromecast-related in this scenario because it looks like Chromecast's timeouts are all related to video which aren't relevant to this use case.

window._setTimeout = window.setTimeout;
window.setTimeout = function(a, b) {
    // disable setTimeout so chromecast won't kill us after 5 minutes...
};

Then in our own app if we need to use a timeout we call _setTimeout instead.

I would be interested if anyone has discovered a better way to achieve this, aside from manually hosting cast_receiver_framework.js with the offending line commented out (which is inside the Wn(a, b) function) or sending a silent clip every few minutes. But self-hosting isn't recommended by Google.

A better solution may be to dig deep in the minified code to work out how Xn(a) is called as that disables the timeout whenever media is playing, and then find a way to call that from within the Chromecast app.

Alessio DF
  • 51
  • 1
  • 4
0

Loading a short inaudible audio clip from the sender to the receiver every 4 minutes seems to do the trick. This should not impact performance much if the file is small. Here is some android code.

    MediaMetadata metadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MUSIC_TRACK);

    MediaInfo mediaInfo = new MediaInfo.Builder("https://some-inaudible-clip.mp3")
            .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
            .setContentType("audio/mpeg")
            .setMetadata(metadata)
            .build();

    RemoteMediaClient remoteMediaClient = castSession.getRemoteMediaClient();

    remoteMediaClient.load(mediaInfo, true);
jdewit
  • 966
  • 9
  • 10
-3

It is possible to send a custom namespace message from the receiver to the sender. That should keep the heartbeat live. However, your use case is not directly supported by the Cast SDK, so you would have to experiment on a solution.

Leon Nicholls
  • 4,623
  • 2
  • 16
  • 17
  • Thanks for your continued support Leon. I've implemented both a custom message from sender to receiver and receiver to sender. Here is a sample receiver log shown to be both sending and receiving custom messages: https://i.imgur.com/IdCOE0f.png. Unfortunately that did not keep the heartbeat live and the receiver continues to terminate the cast session after a timeout (also shown in the log). I'm trying to reverse engineer a solution by starting with the point at which the 5 minute (300 second) timeout occurs in the CAF Receiver Javascript SDK. Is there a version that is not minified available? – Elliott Oct 11 '17 at 11:42
  • The Receiver SDK non-minified source code is not available. You might have to also in addition to the messages also maintain a media session. – Leon Nicholls Oct 11 '17 at 15:46
  • I've spent all day trying to get this to work with another member of the community The timeout appears to be completely down to the [player state](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.messages#.PlayerState) being idle for an uninterrupted 5 minutes. I'm now trying to periodically load a small image into the player every 3 minutes to keep it alive but running into issues with loading an image into the player (raising a question in the [community](https://plus.google.com/communities/115742157569103585450)) – Elliott Oct 11 '17 at 16:42
  • I tried loading a very long video and simply hiding the player so that the underlying [player state](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.messages#.PlayerState) was PLAYING but the performance ground to a halt dealing with that in addition to rendering the content I actually want to show – Elliott Oct 11 '17 at 16:46
  • Custom receivers are not supported in CAF? Really? Is there any documentation as to why? When will support for the receiver sdk v2 end? – jdewit Nov 15 '17 at 16:52
  • You can create a custom receiver using CAF. – Leon Nicholls Nov 15 '17 at 21:25
  • 2
    If you are required to use the standard media player then I would hardly call that custom. Did you ever find a solution to this Elliott? – jdewit Dec 29 '17 at 18:27
  • I'm also wondering this. A custom receiver I have is just disconnecting/closing the connection when migrating from v2 to CAF. It is out of the question of playing a video in the background, since this would defeat the point of migrating from v2 to CAF. – Kasper Rynning-Tønnesen Feb 26 '18 at 20:13
  • @LeonNicholls isn't there any way to manually set the state of media playback? this is rather inconvenient. Is the CAF receiver only meant to be used with the built-in player? – Pierfrancesco Soffritti Jun 23 '18 at 10:14