Chromecast 自定义google cast接收器卡在“中”;“正在加载”;

Chromecast 自定义google cast接收器卡在“中”;“正在加载”;,chromecast,google-cast,Chromecast,Google Cast,我的定制v3 CAF接收器应用程序正在成功播放前几个live&vod资产。之后,由于“加载正在进行”,它进入媒体命令正在排队的状态。它仍然(成功)获取清单,但媒体_状态仍为“缓冲”。然后,日志显示: [4.537s][cast.receiver.MediaManager]正在加载,媒体命令正在排队 [5.893s][cast.receiver.MediaManager]缓冲状态已更改,isPlayerBuffering:真实旧时间:0当前时间:0 [5.897s][cast.receiver.M

我的定制v3 CAF接收器应用程序正在成功播放前几个live&vod资产。之后,由于“加载正在进行”,它进入媒体命令正在排队的状态。它仍然(成功)获取清单,但媒体_状态仍为“缓冲”。然后,日志显示:

[4.537s][cast.receiver.MediaManager]正在加载,媒体命令正在排队

[5.893s][cast.receiver.MediaManager]缓冲状态已更改,isPlayerBuffering:真实旧时间:0当前时间:0

[5.897s][cast.receiver.MediaManager]发送广播状态消息

CastContext核心事件:{“类型”:“媒体状态”,“媒体状态”:{“mediaSessionId”:1,“playbackRate”:1,“playerState”:“缓冲”,“currentTime”:0,“SupportedMediaCommand”:12303,“音量”:{“级别”:1,“静音”:false},“currentItemId”:1,“重复模式”:“重复关闭”,“LiveSeeKablrange”:{“开始”:0,“结束”:20.0009927520752,“isMovingWindow”:true,“IsLivedDone”:false}}}

CastContext媒体状态事件:{“类型”:“媒体状态”,“媒体状态”:{“mediaSessionId”:1,“playbackRate”:1,“playerState”:“缓冲”,“currentTime”:0,“SupportedMediaCommand”:12303,“音量”:{“级别”:1,“静音”:false},“currentItemId”:1,“重复模式”:“重复关闭”,“LiveSeeKabrerange”:{“开始”:0,“结束”:20.0009927520752,“isMovingWindow”:true,“isLiveDone”:false}}

获取已完成加载:获取“(清单url)”

没有显示任何错误

即使在关闭并重新启动强制转换会话后,问题仍然存在。强制转换设备本身必须重新启动才能解决问题。看起来在会话之间保留了数据

值得注意的是,cast receiver应用程序尚未发布。它托管在本地网络上

我的问题是:

  • 这种卡滞行为的原因可能是什么
  • 会话之间是否保留了任何会话数据
  • 如何完全重置cast接收器应用程序,而无需重新启动cast设备
receiver应用程序本身非常基本。除了许可证包装外,它类似于普通示例应用程序:

const { cast } = window;

const TAG = "CastContext";

class CastStore {
  static instance = null;

  error = observable.box();

  framerate = observable.box();

  static getInstance() {
    if (!CastStore.instance) {
      CastStore.instance = new CastStore();
    }
    return CastStore.instance;
  }

  get debugLog() {
    return this.framerate.get();
  }

  get errorLog() {
    return this.error.get();
  }

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

    playerManager.addEventListener(
      cast.framework.events.category.CORE,
      event => {
        console.log(TAG, "Core event: " + JSON.stringify(event));
      }
    );
    playerManager.addEventListener(
      cast.framework.events.EventType.MEDIA_STATUS,
      event => {
        console.log(TAG, "MEDIA_STATUS event: " + JSON.stringify(event));
      }
    );
    playerManager.addEventListener(
      cast.framework.events.EventType.BITRATE_CHANGED,
      event => {
        console.log(TAG, "BITRATE_CHANGED event: " + JSON.stringify(event));
        runInAction(() => {
          this.framerate.set(`bitrate: ${event.totalBitrate}`);
        });
      }
    );
    playerManager.addEventListener(
      cast.framework.events.EventType.ERROR,
      event => {
        console.log(TAG, "ERROR event: " + JSON.stringify(event));
        runInAction(() => {
          this.error.set(`Error detailedErrorCode: ${event.detailedErrorCode}`);
        });
      }
    );

    // intercept the LOAD request to be able to read in a contentId and get data.
    this.loadHandler = new LoadHandler();
    playerManager.setMessageInterceptor(
      cast.framework.messages.MessageType.LOAD,
      loadRequestData => {
        this.framerate.set(null);
        this.error.set(null);

        console.log(TAG, "LOAD message: " + JSON.stringify(loadRequestData));
        if (!loadRequestData.media) {
          const error = new cast.framework.messages.ErrorData(
            cast.framework.messages.ErrorType.LOAD_CANCELLED
          );
          error.reason = cast.framework.messages.ErrorReason.INVALID_PARAM;
          return error;
        }

        if (!loadRequestData.media.entity) {
          // Copy the value from contentId for legacy reasons if needed
          loadRequestData.media.entity = loadRequestData.media.contentId;
        }

        // notify loadMedia
        this.loadHandler.onLoadMedia(loadRequestData, playerManager);
        return loadRequestData;
      }
    );

    const playbackConfig = new cast.framework.PlaybackConfig();

    // intercept license requests & responses
    playbackConfig.licenseRequestHandler = requestInfo => {
      const challenge = requestInfo.content;
      const { castToken } = this.loadHandler;
      const wrappedRequest = DrmLicenseHelper.wrapLicenseRequest(
        challenge,
        castToken
      );
      requestInfo.content = wrappedRequest;
      return requestInfo;
    };
    playbackConfig.licenseHandler = license => {
      const unwrappedLicense = DrmLicenseHelper.unwrapLicenseResponse(license);
      return unwrappedLicense;
    };

    // Duration of buffered media in seconds to start/resume playback after auto-paused due to buffering; default is 10.
    playbackConfig.autoResumeDuration = 4;

    // Minimum number of buffered segments to start/resume playback.
    playbackConfig.initialBandwidth = 1200000;

    context.start({
      touchScreenOptimizedApp: true,
      playbackConfig: playbackConfig,
      supportedCommands: cast.framework.messages.Command.ALL_BASIC_MEDIA
    });
  }
}
LoadHandler可以选择添加一个代理(我正在使用cors anywhere代理删除源标头),并存储licenseRequests的castToken:

class LoadHandler {
  CORS_USE_PROXY = true;
  CORS_PROXY = "http://192.168.0.127:8003";

  castToken = null;

  onLoadMedia(loadRequestData, playerManager) {
    if (!loadRequestData) {
      return;
    }
    const { media } = loadRequestData;

    // disable cors for local testing
    if (this.CORS_USE_PROXY) {
      media.contentId = `${this.CORS_PROXY}/${media.contentId}`;
    }

    const { customData } = media;
    if (customData) {
      const { licenseUrl, castToken } = customData;

      // install cast token
      this.castToken = castToken;

      // handle license URL
      if (licenseUrl) {
        const playbackConfig = playerManager.getPlaybackConfig();
        playbackConfig.licenseUrl = licenseUrl;
        const { contentType } = loadRequestData.media;

        // Dash: "application/dash+xml"
        playbackConfig.protectionSystem = cast.framework.ContentProtection.WIDEVINE;

        // disable cors for local testing
        if (this.CORS_USE_PROXY) {
          playbackConfig.licenseUrl = `${this.CORS_PROXY}/${licenseUrl}`;
        }
      }
    }
  }
}
DrmHelper包装许可证请求以添加castToken,base64对整个请求进行编码。许可证响应随后进行base64解码和取消包装:

export default class DrmLicenseHelper {
  static wrapLicenseRequest(challenge, castToken) {
    const wrapped = {};
    wrapped.AuthToken = castToken;
    wrapped.Payload = fromByteArray(new Uint8Array(challenge));
    const wrappedJson = JSON.stringify(wrapped);
    const wrappedLicenseRequest = fromByteArray(
      new TextEncoder().encode(wrappedJson)
    );
    return wrappedLicenseRequest;
  }

  static unwrapLicenseResponse(license) {
    try {
      const responseString = String.fromCharCode.apply(String, license);
      const responseJson = JSON.parse(responseString);
      const rawLicenseBase64 = responseJson.license;
      const decodedLicense = toByteArray(rawLicenseBase64);
      return decodedLicense;
    } catch (e) {
      return license;
    }
  }
}

cast.framework.messages.MessageType.LOAD的处理程序应始终返回:

  • (可能已修改)
    loadRequestData
    ,或
  • 对(可能修改的)
    loadRequestData的承诺
  • null
    放弃加载请求(我不能100%确定这对加载请求有效)
如果不这样做,加载请求将留在队列中,任何新请求都将在初始请求之后排队


在处理程序中,如果
!loadRequestData.media
,您将返回一个错误,这将使您进入该状态。另一种可能是加载请求处理程序中出现异常,这也将使您处于该状态。

我想我们有不同的方法,在加载内容时,通过
发送消息
发送所有可能的内容e创建一个
new cast.framework.messages.LoadRequestData()
,我们使用
playerManager.load(loadRequest)
加载它

但我想你可能正在集成Chromecast上测试,我们也看到了这个问题

我建议你做一个或多个

  • 对所有响应启用gzip压缩
  • 停止播放
    playerManager.Stop()
    (可能在中间接收器中?)
  • 更改
    licenseUrl
    的设置方式
我们如何设置许可证URL

playerManager.setMediaPlaybackInfoHandler((loadRequestData, playbackConfig) => {
    playbackConfig.licenseUrl = loadRequestData.customData.licenseUrl;
    return playbackConfig;
  }
);

您好。我有类似的问题,但没有修复jet。您能回答以下问题吗:您使用的是什么设备?google cast的版本是什么?媒体格式是什么?您是否定义广告中断?我主要是向内置chromecast的安卓电视盒进行播放。问题似乎不是特定于设备的。chromecast版本目前是1.36.168363。流媒体协议ol为仪表板(和widevine DRM)。没有广告中断。今天我们很幸运地删除了cookie以解除设备阻塞。明天我们将对此进行彻底检查,并提出有关加载处理程序的建议。谢谢。因此,会话是从cookie中保留和还原的?什么是
加载处理程序
?谢谢。除此之外,是否有方法以干净状态启动强制转换会话?这是为什么在结束“卡住”会话后保持状态?我认为不可能从接收器重新启动cast会话以进入干净状态。根据我的经验,CAF在处理截获请求的方式上不是很灵活。处理负载请求尤其令人痛苦,尤其是在需要异步执行的情况下d支持在前一个加载请求尚未完成时处理新的加载请求。这可能是触发卡住的“加载进行中”的另一个场景。。。