【问题标题】:Chrome cast custom receiver "Failed to cast. Please try again"Chrome 投射自定义接收器“投射失败。请重试”
【发布时间】:2021-05-24 23:48:36
【问题描述】:

我正在尝试构建一个 chrome cast 自定义接收器,按照https://codelabs.developers.google.com/codelabs/cast-receiver#0 的教程,我无法让它工作,并且不知道如何开始找出问题所在。我已经注册了我的演员应用程序,并尝试使用https://casttool.appspot.com/cactool/ 来测试接收器。该测试适用于 cactools "CC1AD845" 的样本 ID,它的效果很好。我还注册了我的设备进行测试,并重新启动了设备,我认为这很有效,因为在我这样做之前,cactools 不会显示我的自定义应用程序 ID 的 chromecast 按钮,但现在它确实显示了。问题是当我点击它时,chrome只显示消息:

“投射失败。请重试”

我的接收器使用 http-server 和 ngrok 运行,就像 codelabs 指示的那样。我将 ngrok 服务器的正确路径复制到演员开发者控制台上的应用程序 URL。

当我从浏览器运行 ngrok url 时,devtools 控制台显示以下错误:

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

但我认为这是由于我从浏览器打开时没有设备造成的,并且在 Chromecast 上运行时不应该发生这种情况。

我复制了 codelab 提供的整个文件夹,这应该是自定义接收器的工作示例。但即使使用他们的确切代码,它也不起作用。代码是:

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();

提前谢谢你。

【问题讨论】:

  • 在浏览器上得到了相同的结果:Cannot read property 'is_device_registered' 查看了您的代码,看起来非常好,和我的一样,我的工作正常。当您在浏览器中访问该 URL 时,您是否将您的 Chromecast 应用程序指向正确的 URL 并加载 js 文件?

标签: javascript google-chrome chromecast


【解决方案1】:

“is_device_registered”错误是由无法在浏览器中运行的 Cast Debug Logger 调用引起的(因为它没有在 Chromecast 设备上运行,因此无法找到它)。

经过多次迭代和测试,在使用代码实验室设置时,我注意到“投射失败。请重试”的以下常见原因:

  1. Chromecast 会缓存应用程序数据,因此如果您更新 Receiver App URL,则必须在使用新 URL 之前重新启动设备
  2. Ngrok 会话超时(通过重新启动 ngrok 并在 Cast Developer Console 中更新 Receiver App URL 解决)
  3. 观察您是否在 http-server 控制台中看到来自 Chromecast 的 GET 请求(我的 CC gen 3 在用户代理中有 'Linux armv71' 等)。如果是这样,则 Receiver App 配置正确,您的问题在于代码(HTML、js 等)

【讨论】:

  • 这并不能真正回答问题。如果您有其他问题,可以点击 提问。要在此问题有新答案时收到通知,您可以follow this question。一旦你有足够的reputation,你也可以add a bounty 来引起对这个问题的更多关注。 - From Review
  • 更新了答案,现在更好了吗? :-)
猜你喜欢
  • 2017-12-11
  • 2023-03-24
  • 2015-11-01
  • 1970-01-01
  • 2021-09-12
  • 2022-08-12
  • 2018-11-30
  • 1970-01-01
  • 2012-09-29
相关资源
最近更新 更多