Thiago Pereira Maia
Thiago Pereira Maia

Reputation: 623

Chrome cast custom receiver "Failed to cast. Please try again"

I'm trying to build a chrome cast custom receiver, following the tutorial at https://codelabs.developers.google.com/codelabs/cast-receiver#0 I can't get it to work, and have no idea how to start finding out what is the problem. I have registered my cast app, and am trying to use https://casttool.appspot.com/cactool/ to test the receiver. The test works for the sample ID of cactools "CC1AD845", it casts just fine. I have also registered my device for testing, and restarted the device, which I believe worked, because before I did that, cactools wouldn't show the chromecast button for my custom app id, but now it does show. The problem is that when I click it, chrome just shows the message:

"Failed to cast. Please try again"

I have my receiver running with http-server and ngrok, just like the codelabs instructed. I copied the correct path of the ngrok server to the app URL on the cast developers console.

When I run the ngrok url from the browser, the devtools console show the following error:

Uncaught TypeError: Cannot read property 'is_device_registered' of null
    at new V (caf_receiver_logger.js:18)
    at Function.V.getInstance (caf_receiver_logger.js:28)
    at receiver.js:13

But I believe this is caused by the fact that there is no device when I open from the browser, and this shouldn't happen when running on Chromecast.

I copied the whole folder provided by codelab which is supposedly a working example of a custom receiver. But even using their exact code, it doesn't work. The codes are:

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Cast CAF Receiver</title>
    <script src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
    <!-- Cast Debug Logger -->
    <script src="//www.gstatic.com/cast/sdk/libs/devtools/debug_layer/caf_receiver_logger.js"></script>
  </head>

  <body>
    <cast-media-player></cast-media-player>
    <footer>
      <script src="js/receiver.js"></script>
  </footer>
  </body>
</html>

receiver.js

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

//Media Sample API Values
const SAMPLE_URL = "https://storage.googleapis.com/cpe-sample-media/content.json";
const StreamType = {
  DASH: 'application/dash+xml',
  HLS: 'application/x-mpegurl'
}
const TEST_STREAM_TYPE = StreamType.DASH

// Debug Logger
const castDebugLogger = cast.debug.CastDebugLogger.getInstance();
const LOG_TAG = 'MyAPP.LOG';

// Enable debug logger and show a 'DEBUG MODE' overlay at top left corner.
castDebugLogger.setEnabled(true);

// Show debug overlay
// castDebugLogger.showDebugLogs(true);

// Set verbosity level for Core events.
castDebugLogger.loggerLevelByEvents = {
  'cast.framework.events.category.CORE': cast.framework.LoggerLevel.INFO,
  'cast.framework.events.EventType.MEDIA_STATUS': cast.framework.LoggerLevel.DEBUG
}

// Set verbosity level for custom tags.
castDebugLogger.loggerLevelByTags = {
    LOG_TAG: cast.framework.LoggerLevel.DEBUG,
};

function makeRequest (method, url) {
  return new Promise(function (resolve, reject) {
    let xhr = new XMLHttpRequest();
    xhr.open(method, url);
    xhr.onload = function () {
      if (this.status >= 200 && this.status < 300) {
        resolve(JSON.parse(xhr.response));
      } else {
        reject({
          status: this.status,
          statusText: xhr.statusText
        });
      }
    };
    xhr.onerror = function () {
      reject({
        status: this.status,
        statusText: xhr.statusText
      });
    };
    xhr.send();
  });
}

playerManager.setMessageInterceptor(
  cast.framework.messages.MessageType.LOAD,
  request => {
    castDebugLogger.info(LOG_TAG, 'Intercepting LOAD request');

    // Map contentId to entity
    if (request.media && request.media.entity) {
      request.media.contentId = request.media.entity;
    }

    return new Promise((resolve, reject) => {
      // Fetch repository metadata
      makeRequest('GET', SAMPLE_URL)
        .then(function (data) {
          // Obtain resources by contentId from downloaded repository metadata.
          let item = data[request.media.contentId];
          if(!item) {
            // Content could not be found in repository
            castDebugLogger.error(LOG_TAG, 'Content not found');
            reject();
          } else {
            // Adjusting request to make requested content playable
            request.media.contentType = TEST_STREAM_TYPE;

            // Configure player to parse DASH content
            if(TEST_STREAM_TYPE == StreamType.DASH) {
              request.media.contentUrl = item.stream.dash;
            }

            // Configure player to parse HLS content
            else if(TEST_STREAM_TYPE == StreamType.HLS) {
              request.media.contentUrl = item.stream.hls
              request.media.hlsSegmentFormat = cast.framework.messages.HlsSegmentFormat.FMP4;
              request.media.hlsVideoSegmentFormat = cast.framework.messages.HlsVideoSegmentFormat.FMP4;
            }
            
            castDebugLogger.warn(LOG_TAG, 'Playable URL:', request.media.contentUrl);
            
            // Add metadata
            let metadata = new cast.framework.messages.GenericMediaMetadata();
            metadata.title = item.title;
            metadata.subtitle = item.author;

            request.media.metadata = metadata;

            // Resolve request
            resolve(request);
          }
      });
    });
  });

// Optimizing for smart displays
const touchControls = cast.framework.ui.Controls.getInstance();
const playerData = new cast.framework.ui.PlayerData();
const playerDataBinder = new cast.framework.ui.PlayerDataBinder(playerData);

let browseItems = getBrowseItems();

function getBrowseItems() {
  let browseItems = [];
  makeRequest('GET', SAMPLE_URL)
  .then(function (data) {
    for (let key in data) {
      let item = new cast.framework.ui.BrowseItem();
      item.entity = key;
      item.title = data[key].title;
      item.subtitle = data[key].description;
      item.image = new cast.framework.messages.Image(data[key].poster);
      item.imageType = cast.framework.ui.BrowseImageType.MOVIE;
      browseItems.push(item);
    }
  });
  return browseItems;
}

let browseContent = new cast.framework.ui.BrowseContent();
browseContent.title = 'Up Next';
browseContent.items = browseItems;
browseContent.targetAspectRatio =
  cast.framework.ui.BrowseImageAspectRatio.LANDSCAPE_16_TO_9;

playerDataBinder.addEventListener(
  cast.framework.ui.PlayerDataEventType.MEDIA_CHANGED,
  (e) => {
    if (!e.value) return;

    // Media browse
    touchControls.setBrowseContent(browseContent);

    // Clear default buttons and re-assign
    touchControls.clearDefaultSlotAssignments();
    touchControls.assignButton(
      cast.framework.ui.ControlsSlot.SLOT_PRIMARY_1,
      cast.framework.ui.ControlsButton.SEEK_BACKWARD_30
    );
  });

context.start();

Thank you in advance.

Upvotes: 3

Views: 1383

Answers (1)

Andreas Dahlstr&#246;m
Andreas Dahlstr&#246;m

Reputation: 149

The "is_device_registered" error is caused by the Cast Debug Logger calls which doesn't work to run in a browser (since it isn't running on the Chromecast device and thus can't find it).

After many iterations and testing I have noticed the following common causes for "Failed to cast. Please try again" while using the code labs setup:

  1. Chromecast caches the application data, so if you for instance updates the Receiver App URL the device has to be restarted before the new URL is used
  2. Ngrok session times out (solved by restarting ngrok and updating the Receiver App URL in the Cast Developer Console)
  3. Observe if you see GET requests from the Chromecast in the http-server console (my CC gen 3 has 'Linux armv71' etc in User Agent). If so, the Receiver App is configured correctly and your issue is with the code (HTML, js etc)

Upvotes: 3

Related Questions