默认命名空间在 chromecast 接收器中调用

Posted

技术标签:

【中文标题】默认命名空间在 chromecast 接收器中调用【英文标题】:Default namespace is calling in chromecast receiver 【发布时间】:2020-04-02 05:40:37 【问题描述】:

我无法接收来自发件人的消息,我尝试了很多,在调试时我得到了这样的日志 onMessageSendFailed: com.google.android.gms urn:x-cast:com.google.cast.media 2 INVALID_REQUEST,我不知道问题出在哪里,这是问题吗?谁能帮我解决这个问题以及如何在发送者和接收者之间建立通信......

<!DOCTYPE html>
<html>
  <head>
    <title>CAF Receiver</title>
  </head>
  <body>
    <cast-media-player id="player"></cast-media-player>
    <style>
      #player 
        --theme-hue: 210;
        --splash-image: url("https://gist.githubusercontent.com/devcer/523f183fadefb3fdf5dccb761198294b/raw/fedfca68e6af85eb1e317340500a791130ddd1fe/rh-cast.png");
        --splash-background: #3d4246;
        --logo-image: url("https://gist.githubusercontent.com/devcer/523f183fadefb3fdf5dccb761198294b/raw/4bc55d8eebf018f27e7e1e32bbb0a337101bf005/logo.png");
        --watermark-image: url("https://gist.githubusercontent.com/devcer/523f183fadefb3fdf5dccb761198294b/raw/4bc55d8eebf018f27e7e1e32bbb0a337101bf005/logo.png");
        --watermark-size: 200px 200px;
        --progress-color: #f15a28;
        --font-family: Roboto;
      
    </style>
    <script
      type="text/javascript"
      src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"
    ></script>
    <script>
      const context = cast.framework.CastReceiverContext.getInstance();
      const playerManager = context.getPlayerManager();
      const options = new cast.framework.CastReceiverOptions();
      // options.maxInactivity = 3600;

      // message interceptor
      const CUSTOM_CHANNEL = "urn:x-cast:com.custApp";
      context.addCustomMessageListener(CUSTOM_CHANNEL, function(customEvent) 
        // handle customEvent.
        console.log("addCustomMessageListener: " + customEvent);
      );

      // intercept the LOAD request to be able to read in a contentId and get data
      playerManager.setMessageInterceptor(
        cast.framework.messages.MessageType.LOAD,
        loadRequestData => 
          debugger;
          console.log("loadRequestData" + loadRequestData);
          return loadRequestData;
        
      );

      // listen to all Core Events
      playerManager.addEventListener(
        cast.framework.events.category.CORE,
        event => 
          console.log("playerManager");
        
      );

      playerManager.addEventListener(
        cast.framework.events.EventType.PLAYER_LOAD_COMPLETE,
        () => 
          console.log("PLAYER_LOAD_COMPLETE");
        
      );

      const playbackConfig = new cast.framework.PlaybackConfig();
      playbackConfig.manifestRequestHandler = requestInfo => 
        console.log("requestInfo" + requestInfo);
      ;

      playbackConfig.segmentRequestHandler = requestInfo => 
        console.log("segmentRequestHandler: " + requestInfo);
      ;

      // Sets the player to start playback as soon as there are five seconds of
      // media contents buffered. Default is 10.
      playbackConfig.autoResumeDuration = 5;
      context.sendCustomMessage(CUSTOM_CHANNEL, "message from receiver");
      context.start( playbackConfig: playbackConfig );
    </script>
  </body>
</html>
 Run code snippet

【问题讨论】:

【参考方案1】:

Yesh Yesh...its very simple.事实上,its extremely simply.事实上,simpler than anything you can ever imagine.

      // TODO: Start time, is a fake timestamp. Use correct values for your implementation.
      let currentTime = new Date();
      // Convert from milliseconds to seconds.
      currentTime = currentTime / 1000;
      let sectionStartAbsoluteTime = currentTime;

      // Duration should be -1 for live streams.
      mediaInfo.duration = -1;
      // TODO: Set on the receiver for your implementation.
      mediaInfo.startAbsoluteTime = currentTime;
      mediaInfo.metadata.sectionStartAbsoluteTime = sectionStartAbsoluteTime;
      // TODO: Set on the receiver for your implementation.
      mediaInfo.metadata.sectionStartTimeInMedia = 0;
      mediaInfo.metadata.sectionDuration = this.mediaContents[mediaIndex]['duration'];

      let item = new chrome.cast.media.QueueItem(mediaInfo);
      request.queueData = new chrome.cast.media.QueueData();
      request.queueData.items = [item];
      request.queueData.name = "Sample Queue for Live";
    

    // Do not immediately start playing if the player was previously PAUSED.
    if (!this.playerStateBeforeSwitch || this.playerStateBeforeSwitch == PLAYER_STATE.PAUSED) 
      request.autoplay = false;
     else 
      request.autoplay = true;
    

    cast.framework.CastContext.getInstance().getCurrentSession().loadMedia(request).then(
      function () 
        console.log('Remote media loaded');
      .bind(this),
      function (errorCode) 
        this.playerState = PLAYER_STATE.IDLE;
        console.log('Remote media load error: ' +
          CastPlayer.getErrorMessage(errorCode));
        this.playerHandler.updateDisplay();
      .bind(this));
  .bind(this);

  playerTarget.isMediaLoaded = function (mediaIndex) 
    let session = cast.framework.CastContext.getInstance().getCurrentSession();
    if (!session) return false;

    let media = session.getMediaSession();
    if (!media) return false;

    if (media.playerState == PLAYER_STATE.IDLE) 
      return false;
    

    // No need to verify local mediaIndex content.
    return true;
  .bind(this);

  /**
   * @return number? Current media time for the content. Always returns
   *      media time even if in clock time (conversion done when displaying).
   */
  playerTarget.getCurrentMediaTime = function () 
    if (this.isLiveContent && this.mediaInfo.metadata &&
      this.mediaInfo.metadata.sectionStartTimeInMedia) 
      return this.remotePlayer.currentTime - this.mediaInfo.metadata.sectionStartTimeInMedia;
     else 
      // VOD and live scenerios where live metadata is not provided.
      return this.remotePlayer.currentTime;
    
  .bind(this);

  /**
   * @return number? media time duration for the content. Always returns
   *      media time even if in clock time (conversion done when displaying).
   */
  playerTarget.getMediaDuration = function () 
    if (this.isLiveContent) 
      // Scenerios when live metadata is not provided.
      if (this.mediaInfo.metadata == undefined ||
        this.mediaInfo.metadata.sectionDuration == undefined ||
        this.mediaInfo.metadata.sectionStartTimeInMedia == undefined) 
        return null;
      

      return this.mediaInfo.metadata.sectionDuration;
     else 
      return this.remotePlayer.duration;
    
  .bind(this);

  playerTarget.updateDisplay = function () 
    let castSession = cast.framework.CastContext.getInstance().getCurrentSession();
    if (castSession && castSession.getMediaSession() && castSession.getMediaSession().media) 
      let media = castSession.getMediaSession();
      let mediaInfo = media.media;

      // image placeholder for video view
      var vi = document.getElementById('video_image');
      if (mediaInfo.metadata && mediaInfo.metadata.images &&
        mediaInfo.metadata.images.length > 0) 
        vi.src = mediaInfo.metadata.images[0].url;
      

      // playerstate view
      document.getElementById('playerstate').style.display = 'block';
      document.getElementById('playerstatebg').style.display = 'block';
      document.getElementById('video_image_overlay').style.display = 'block';

      let mediaTitle = '';
      let mediaEpisodeTitle = '';
      let mediaSubtitle = '';

      if (mediaInfo.metadata) 
        mediaTitle = mediaInfo.metadata.title;
        mediaEpisodeTitle = mediaInfo.metadata.episodeTitle;
        // Append episode title if present
        mediaTitle = mediaEpisodeTitle ? mediaTitle + ': ' + mediaEpisodeTitle : mediaTitle;
        // Do not display mediaTitle if not defined.
        mediaTitle = (mediaTitle) ? mediaTitle + ' ' : '';
        mediaSubtitle = mediaInfo.metadata.subtitle;
        mediaSubtitle = (mediaSubtitle) ? mediaSubtitle + ' ' : '';
      

      if (DEMO_MODE) 
        document.getElementById('playerstate').innerHTML =
          (ENABLE_LIVE ? 'Live Content ' : 'Sample Video ') + media.playerState + ' on Chromecast';

        // media_info view
        document.getElementById('media_title').innerHTML = (ENABLE_LIVE ? 'Live Content' : 'Sample Video');
        document.getElementById('media_subtitle').innerHTML = '';
       else 
        document.getElementById('playerstate').innerHTML =
          mediaTitle + media.playerState + ' on ' +
          castSession.getCastDevice().friendlyName;

        // media_info view
        document.getElementById('media_title').innerHTML = mediaTitle;
        document.getElementById('media_subtitle').innerHTML = mediaSubtitle;
      

      // live information
      if (mediaInfo.streamType == chrome.cast.media.StreamType.LIVE) 
        this.liveSeekableRange = media.liveSeekableRange;

        let live_indicator = document.getElementById('live_indicator');
        live_indicator.style.display = 'block';

        // Display indicator if current time is close to the end of
        // the seekable range.
        if (this.liveSeekableRange && (Math.abs(media.getEstimatedTime() - this.liveSeekableRange.end) < LIVE_INDICATOR_BUFFER)) 
          live_indicator.src = "imagefiles/live_indicator_active.png";
         else 
          live_indicator.src = "imagefiles/live_indicator_inactive.png";
        
       else 
        document.getElementById('live_indicator').style.display = 'none';
      
     else 
      // playerstate view
      document.getElementById('playerstate').style.display = 'none';
      document.getElementById('playerstatebg').style.display = 'none';
      document.getElementById('video_image_overlay').style.display = 'none';

      // media_info view
      document.getElementById('media_title').innerHTML = "";
      document.getElementById('media_subtitle').innerHTML = "";
    
  .bind(this);

  playerTarget.updateCurrentTimeDisplay = function () 
    this.playerHandler.setTimeString(document.getElementById('currentTime'), this.playerHandler.getCurrentMediaTime());
  .bind(this);

  playerTarget.updateDurationDisplay = function () 
    this.playerHandler.setTimeString(document.getElementById('duration'), this.playerHandler.getMediaDuration());
  .bind(this);

  playerTarget.setTimeString = function (element, time) 
    let currentTimeString = this.getMediaTimeString(time);

    if (this.isLiveContent) 
      if (currentTimeString == null) 
        element.style.display = 'none';
        return;
      

      // clock time
      if (this.mediaInfo.metadata && this.mediaInfo.metadata.sectionStartAbsoluteTime !== undefined) 
        element.style.display = 'flex';
        element.innerHTML = this.getClockTimeString(time + this.mediaInfo.metadata.sectionStartAbsoluteTime);
       else 
        // media time
        element.style.display = 'flex';
        element.innerHTML = currentTimeString;
      
     else 
      if (currentTimeString !== null) 
        element.style.display = 'flex';
        element.innerHTML = currentTimeString;
       else 
        element.style.display = 'none';
      
    
  .bind(this);

  playerTarget.setVolume = function (volumeSliderPosition) 
    var currentVolume = this.remotePlayer.volumeLevel;
    var p = document.getElementById('audio_bg_level');
    if (volumeSliderPosition < FULL_VOLUME_HEIGHT) 
      p.style.height = volumeSliderPosition + 'px';
      p.style.marginTop = -volumeSliderPosition + 'px';
      currentVolume = volumeSliderPosition / FULL_VOLUME_HEIGHT;
     else 
      currentVolume = 1;
    
    this.remotePlayer.volumeLevel = currentVolume;
    this.remotePlayerController.setVolumeLevel();
  .bind(this);

  playerTarget.mute = function () 
    if (!this.remotePlayer.isMuted) 
      this.remotePlayerController.muteOrUnmute();
    
  .bind(this);

  playerTarget.unMute = function () 
    if (this.remotePlayer.isMuted) 
      this.remotePlayerController.muteOrUnmute();
    
  .bind(this);

  playerTarget.isMuted = function () 
    return this.remotePlayer.isMuted;
  .bind(this);

  playerTarget.seekTo = function (time) 
    this.remotePlayer.currentTime = time;
    this.remotePlayerController.seek();
  .bind(this);

  this.playerHandler.setTarget(playerTarget);

  // Setup remote player properties on setup
  if (this.remotePlayer.isMuted) 
    this.playerHandler.mute();
  
  this.enableProgressBar(this.remotePlayer.canSeek);
  // The remote player may have had a volume set from previous playback
  var currentVolume = this.remotePlayer.volumeLevel * FULL_VOLUME_HEIGHT;
  var p = document.getElementById('audio_bg_level');
  p.style.height = currentVolume + 'px';
  p.style.marginTop = -currentVolume + 'px';

  // Show media_control
  document.getElementById('media_control').style.opacity = 0.7;

  this.hideFullscreenButton();

  // If resuming a session, take the remote properties and continue the existing
  // playback. Otherwise, load local content.
  if (cast.framework.CastContext.getInstance().getCurrentSession().getSessionState() ==
    cast.framework.SessionState.SESSION_RESUMED) 
    console.log('Resuming session');
    this.playerHandler.prepareToPlay();

    // New media has been loaded so the previous ad markers should
    // be removed.
    this.removeAdMarkers();
    this.updateAdMarkers();
   else 
    this.playerHandler.load();
  
;

/**
 * Callback when media is loaded in local player
 */
CastPlayer.prototype.onMediaLoadedLocally = function () 
  var localPlayer = document.getElementById('video_element');
  localPlayer.currentTime = this.currentMediaTime;

  this.playerHandler.prepareToPlay();
;

/**
 * Select a media content
 * @param number mediaIndex A number for media index
 */
CastPlayer.prototype.selectMedia = function (mediaIndex) 
  console.log('Media index selected: ' + mediaIndex);

  this.currentMediaIndex = mediaIndex;
  // Clear currentMediaInfo when playing content from the sender.
  this.playerHandler.currentMediaInfo = undefined;

  // Set video image
  var vi = document.getElementById('video_image');
  vi.src = MEDIA_SOURCE_ROOT + this.mediaContents[mediaIndex]['thumb'];

  // Reset progress bar
  var pi = document.getElementById('progress_indicator');
  pi.style.marginLeft = '0px';
  var progress = document.getElementById('progress');
  progress.style.width = '0px';

  let seekable_window = document.getElementById('seekable_window');
  let unseekable_overlay = document.getElementById('unseekable_overlay');
  seekable_window.style.width = PROGRESS_BAR_WIDTH;
  unseekable_overlay.style.width = '0px';

  // Stop timer and reset time displays
  this.stopProgressTimer();
  this.currentMediaTime = 0;
  this.playerHandler.setTimeString(document.getElementById('currentTime'), 0);
  this.playerHandler.setTimeString(document.getElementById('duration'), 0);

  this.playerState = PLAYER_STATE.IDLE;
  this.playerHandler.play();
;

/**
 * Media seek function
 * @param Event event An event object from seek
 */
CastPlayer.prototype.seekMedia = function (event) 
  if (this.mediaDuration == null || (cast.framework.CastContext.getInstance().getCurrentSession() && !this.remotePlayer.canSeek)) 
    console.log('Error - Not seekable');
    return;
  

  if (this.isLiveContent && !this.liveSeekableRange) 
    console.log('Live content has no seekable range.')
    return;
  

  var position = parseInt(event.offsetX, 10);
  var pi = document.getElementById('progress_indicator');
  var progress = document.getElementById('progress');
  let seekTime = 0;
  let pp = 0;
  let pw = 0;
  if (event.currentTarget.id == 'progress_indicator') 
    seekTime = parseInt(this.currentMediaTime + this.mediaDuration * position /
      PROGRESS_BAR_WIDTH, 10);
    pp = parseInt(pi.style.marginLeft, 10) + position;
    pw = parseInt(progress.style.width, 10) + position;
   else 
    seekTime = parseInt(position * this.mediaDuration / PROGRESS_BAR_WIDTH, 10);
    pp = position;
    pw = position;
  

  if (this.playerState === PLAYER_STATE.PLAYING ||
    this.playerState === PLAYER_STATE.PAUSED) 
    this.currentMediaTime = seekTime;
    progress.style.width = pw + 'px';
    pi.style.marginLeft = pp + 'px';
  

  if (this.isLiveContent) 
    seekTime += this.mediaInfo.metadata.sectionStartTimeInMedia;
  

  this.playerHandler.seekTo(seekTime);
;

/**
 * Set current player volume
 * @param Event mouseEvent
 */
CastPlayer.prototype.setVolume = function (mouseEvent) 
  var p = document.getElementById('audio_bg_level');
  var pos = 0;
  if (mouseEvent.currentTarget.id === 'audio_bg_track') 
    pos = FULL_VOLUME_HEIGHT - parseInt(mouseEvent.offsetY, 10);
   else 
    pos = parseInt(p.clientHeight, 10) - parseInt(mouseEvent.offsetY, 10);
  
  this.playerHandler.setVolume(pos);
;

/**
 * Starts the timer to increment the media progress bar
 */
CastPlayer.prototype.startProgressTimer = function () 
  this.stopProgressTimer();

  // Start progress timer
  this.timer = setInterval(this.incrementMediaTimeHandler, TIMER_STEP);
;

/**
 * Stops the timer to increment the media progress bar
 */
CastPlayer.prototype.stopProgressTimer = function () 
  if (this.timer) 
    clearInterval(this.timer);
    this.timer = null;
  
;

/**
 * Increment media current time depending on remote or local playback
 */
CastPlayer.prototype.incrementMediaTime = function () 
  // First sync with the current player's time
  this.currentMediaTime = this.playerHandler.getCurrentMediaTime();
  this.mediaDuration = this.playerHandler.getMediaDuration();

  this.playerHandler.updateDurationDisplay();

  if (this.mediaDuration == null || this.currentMediaTime < this.mediaDuration || this.isLiveContent) 
    this.playerHandler.updateCurrentTimeDisplay();
    this.updateProgressBarByTimer();
   else if (this.mediaDuration > 0) 
    this.endPlayback();
  
;

/**
 * Update progress bar and currentTime based on timer
 */
CastPlayer.prototype.updateProgressBarByTimer = function () 
  var progressBar = document.getElementById('progress');
  var pi = document.getElementById('progress_indicator');

  // Live situation where the progress and duration is unknown.
  if (this.mediaDuration == null) 
    if (!this.isLiveContent) 
      console.log('Error - Duration is not defined for a VOD stream.');
    

    progressBar.style.width = '0px';
    document.getElementById('skip').style.display = 'none';
    pi.style.display = 'none';

    let seekable_window = document.getElementById('seekable_window');
    let unseekable_overlay = document.getElementById('unseekable_overlay');
    seekable_window.style.width = '0px';
    unseekable_overlay.style.width = '0px';
    return;
   else 
    pi.style.display = '';
  

  if (isNaN(parseInt(progressBar.style.width, 10))) 
    progressBar.style.width = '0px';
  

  // Prevent indicator from exceeding the max width. Happens during
  // short media when each progress step is large
  var pp = Math.floor(PROGRESS_BAR_WIDTH * this.currentMediaTime / this.mediaDuration);
  if (pp > PROGRESS_BAR_WIDTH) 
    pp = PROGRESS_BAR_WIDTH;
   else if (pp < 0) 
    pp = 0;
  

  progressBar.style.width = pp + 'px';
  pi.style.marginLeft = pp + 'px';

  let seekable_window = document.getElementById('seekable_window');
  let unseekable_overlay = document.getElementById('unseekable_overlay');
  if (this.isLiveContent) 
    if (this.liveSeekableRange) 
      // Use the liveSeekableRange to draw the seekable and unseekable windows
      let seekableMediaPosition = Math.max(this.mediaInfo.metadata.sectionStartTimeInMedia, this.liveSeekableRange.end) -
        this.mediaInfo.metadata.sectionStartTimeInMedia;
      let seekableWidth = Math.floor(PROGRESS_BAR_WIDTH * seekableMediaPosition / this.mediaDuration);
      if (seekableWidth > PROGRESS_BAR_WIDTH) 
        seekableWidth = PROGRESS_BAR_WIDTH;
       else if (seekableWidth < 0) 
        seekableWidth = 0;
      
      seekable_window.style.width = seekableWidth + 'px';

      let unseekableMediaPosition = Math.max(this.mediaInfo.metadata.sectionStartTimeInMedia, this.liveSeekableRange.start) -
        this.mediaInfo.metadata.sectionStartTimeInMedia;
      let unseekableWidth = Math.floor(PROGRESS_BAR_WIDTH * unseekableMediaPosition / this.mediaDuration);
      if (unseekableWidth > PROGRESS_BAR_WIDTH) 
        unseekableWidth = PROGRESS_BAR_WIDTH;
       else if (unseekableWidth < 0) 
        unseekableWidth = 0;
      
      unseekable_overlay.style.width = unseekableWidth + 'px';
     else 
      // Nothing is seekable if no liveSeekableRange
      seekable_window.style.width = '0px';
      unseekable_overlay.style.width = PROGRESS_BAR_WIDTH + 'px';
    
   else 
    // Default to everything seekable
    seekable_window.style.width = PROGRESS_BAR_WIDTH + 'px';
    unseekable_overlay.style.width = '0px';
  

  if (pp >= PROGRESS_BAR_WIDTH && !this.isLiveContent) 
    this.endPlayback();
  
;

/**
 *  End playback. Called when media ends.
 */
CastPlayer.prototype.endPlayback = function () 
  this.currentMediaTime = 0;
  this.stopProgressTimer();
  this.playerState = PLAYER_STATE.IDLE;
  this.playerHandler.updateDisplay();

  document.getElementById('play').style.display = 'block';
  document.getElementById('pause').style.display = 'none';
;

/**
 * @param ?number timestamp Linux timestamp
 * @return ?string media time string. Null if time is invalid.
 */
CastPlayer.prototype.getMediaTimeString = function (timestamp) 
  if (timestamp == undefined || timestamp == null) 
    return null;
  

  let isNegative = false;
  if (timestamp < 0) 
    isNegative = true;
    timestamp *= -1;
  

  let hours = Math.floor(timestamp / 3600);
  let minutes = Math.floor((timestamp - (hours * 3600)) / 60);
  let seconds = Math.floor(timestamp - (hours * 3600) - (minutes * 60));

  if (hours < 10) hours = '0' + hours;
  if (minutes < 10) minutes = '0' + minutes;
  if (seconds < 10) seconds = '0' + seconds;

  return (isNegative ? '-' : '') + hours + ':' + minutes + ':' + seconds;
;

/**
 * @param number timestamp Linux timestamp
 * @return ?string ClockTime string. Null if time is invalid.
 */
CastPlayer.prototype.getClockTimeString = function (timestamp) 
  if (!timestamp) return "0:00:00";

  let date = new Date(timestamp * 1000);
  let hours = date.getHours();
  let minutes = date.getMinutes();
  let seconds = date.getSeconds();
  let ampm = hours >= 12 ? 'PM' : 'AM';
  hours = hours % 12;
  // Hour '0' should be '12'
  hours = hours ? hours : 12;
  minutes = ('0' + minutes).slice(-2);
  seconds = ('0' + seconds).slice(-2);
  let clockTime = hours + ':' + minutes + ':' + seconds + ' ' + ampm;
  return clockTime;
;

/**
 * Updates Ad markers in UI
 */
CastPlayer.prototype.updateAdMarkers = function () 
  let castSession = cast.framework.CastContext.getInstance().getCurrentSession();
  if (!castSession) return;

  let media = castSession.getMediaSession();
  if (!media) return;

  let mediaInfo = media.media;
  if (!mediaInfo) return;

  let breaks = mediaInfo.breaks;
  let contentDuration = mediaInfo.duration;

  if (!breaks) 
    return;
  

  for (var i = 0; i < breaks.length; i++) 
    let adBreak = breaks[i];

    // Server-side stitched Ads (embedded) are skipped when the position is beyond
    // the duration, so they shouldn't be shown with an ad marker on the UI.
    if (adBreak.position > contentDuration && adBreak.isEmbedded) 
      continue;
    

    // Place marker if not already set in position
    if (!document.getElementById('ad' + adBreak.position)) 
      var div = document.getElementById('progress')
      div.innerHTML += '<div class="adMarker" id="ad' + adBreak.position +
        '" style="margin-left: ' +
        this.adPositionToMargin(adBreak.position, contentDuration) + 'px"></div>';
    
  
;

/**
 * Remove Ad markers in UI
 */
CastPlayer.prototype.removeAdMarkers = function () 
  document.querySelectorAll('.adMarker').forEach(function (adMarker) 
    adMarker.remove();
  );
;

/**
 * Position of the ad marker from the margin
 */
CastPlayer.prototype.adPositionToMargin = function (position, contentDuration) 
  // Post-roll
  if (position == -1) 
    return PROGRESS_BAR_WIDTH;
  

  // Client stitched Ads (not embedded) beyond the duration, will play at the
  // end of the content.
  if (position > contentDuration) 
    return PROGRESS_BAR_WIDTH;
  

  // Convert Ad position to margin
  return (PROGRESS_BAR_WIDTH * position) / contentDuration;
;

/**
 * Handle BREAK_CLIP_ID_CHANGED event
 */
CastPlayer.prototype.onBreakClipIdChanged = function () 
  // Hide skip button when switching to a new breakClip
  document.getElementById('skip').style.display = 'none';
;

  document.getElementById('progress_indicator').draggable = true;

  // Set up feature radio buttons
  let noneRadio = document.getElementById('none');
  noneRadio.onclick = function () 
    ENABLE_LIVE = false;
    ENABLE_ADS = false;
    console.log("Features have been removed");
  
  let adsRadio = document.getElementById('ads');
  adsRadio.onclick = function () 
    ENABLE_LIVE = false;
    ENABLE_ADS = true;
    console.log("Ads have been enabled");
  
  let liveRadio = document.getElementById('live');
  liveRadio.onclick = function () 
    ENABLE_LIVE = true;
    ENABLE_ADS = false;
    console.log("Live has been enabled");
  

  if (ENABLE_ADS) 
    if (ENABLE_LIVE) 
      console.error('Only one feature can be enabled at a time. Enabling ads.');
    
    adsRadio.checked = true;
    console.log("Ads are enabled");
   else if (ENABLE_LIVE) 
    liveRadio.checked = true;
    console.log("Live is enabled");
   else 
    noneRadio.checked = true;
    console.log("No features are enabled");
  
;

/**
 * Add video thumbnails div's to UI for media JSON contents
 */
CastPlayer.prototype.addVideoThumbs = function () 
  this.mediaContents = mediaJSON['categories'][0]['videos'];
  var ni = document.getElementById('carousel');
  var newdiv = null;
  var divIdName = null;
  for (var i = 0; i < this.mediaContents.length; i++) 
    newdiv = document.createElement('div');
    divIdName = 'thumb' + i + 'Div';
    newdiv.setAttribute('id', divIdName);
    newdiv.setAttribute('class', 'thumb');
    newdiv.innerHTML =
      '<img src="' + MEDIA_SOURCE_ROOT + this.mediaContents[i]['thumb'] +
      '" class="thumbnail">';
    newdiv.addEventListener('click', this.selectMedia.bind(this, i));
    ni.appendChild(newdiv);
  
;

/**
 * Makes human-readable message from chrome.cast.Error
 * @param chrome.cast.Error error
 * @return string error message
 */
CastPlayer.getErrorMessage = function (error) 
  switch (error.code) 
    case chrome.cast.ErrorCode.API_NOT_INITIALIZED:
      return 'The API is not initialized.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.CANCEL:
      return 'The operation was canceled by the user' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.CHANNEL_ERROR:
      return 'A channel to the receiver is not available.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.EXTENSION_MISSING:
      return 'The Cast extension is not available.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.INVALID_PARAMETER:
      return 'The parameters to the operation were not valid.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.RECEIVER_UNAVAILABLE:
      return 'No receiver was compatible with the session request.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.SESSION_ERROR:
      return 'A session could not be created, or a session was invalid.' +
        (error.description ? ' :' + error.description : '');
    case chrome.cast.ErrorCode.TIMEOUT:
      return 'The operation timed out.' +
        (error.description ? ' :' + error.description : '');
    default:
      return error;
  
;

let castPlayer = new CastPlayer();
window['__onGCastApiAvailable'] = function (isAvailable) 
  if (isAvailable) 
    castPlayer.initializeCastPlayer();
  
;

【讨论】:

感谢@bluejayke,但我需要使用 CastReceiverContext 处理接收器部分

以上是关于默认命名空间在 chromecast 接收器中调用的主要内容,如果未能解决你的问题,请参考以下文章

默认接收器上的 Chromecast WebVTT 字幕

使用默认接收器时如何调试 chromecast?

Chromecast v3|安卓 |如何使用自定义命名空间?

通过 Cast.CastApi.sendMessage 向 Chromecast 默认媒体接收器发送啥有效负载

来自android的Chromecast不使用样式接收器

ChromeCast 音频固件 1.24.88047 坏了?