我的自定义v3 CAF接收器应用程序正在成功播放前几场直播和点播资产。之后,由于“正在加载”,因此进入了媒体命令排队的状态。它仍然(成功)正在获取清单,但是MEDIA_STATUS仍然是“缓冲”。日志随后显示:
[4.537s] [cast.receiver.MediaManager]正在进行加载,媒体命令正在排队。
[5.893s] [cast.receiver.MediaManager]缓冲状态已更改,isPlayerBuffering:正确的旧时间:0当前时间:0
[5.897s] [cast.receiver.MediaManager]发送广播状态消息
CastContext核心事件:{“ type”:“ MEDIA_STATUS”,“ mediaStatus”:{“ mediaSessionId”:1,“ playbackRate”:1,“ playerState”:“ BUFFERING”,“ currentTime”:0,“ supportedMediaCommands” :12303,“ volume”:{“ level”:1,“ muted”:false},“ currentItemId”:1,“ repeatMode”:“ REPEAT_OFF”,“ liveSeekableRange”:{“ start”:0,“ end”: 20.000999927520752,“ isMovingWindow”:true,“ isLiveDone”:false}}}
CastContext MEDIA_STATUS事件:{“ type”:“ MEDIA_STATUS”,“ mediaStatus”:{“ mediaSessionId”:1,“ playbackRate”:1,“ playerState”:“ BUFFERING”,“ currentTime”:0,“ supportedMediaCommands” :12303,“ volume”:{“ level”:1,“ muted”:false},“ currentItemId”:1,“ repeatMode”:“ REPEAT_OFF”,“ liveSeekableRange”:{“ start”:0,“ end”: 20.000999927520752,“ isMovingWindow”:true,“ isLiveDone”:false}}}
获取完成的加载:获取“(清单网址)”。
没有显示错误。
即使在关闭并重新启动投射会话之后,问题仍然存在。投放设备本身必须重新启动才能解决。看起来数据在会话之间保留。
请务必注意,投射接收器应用尚未发布。它托管在本地网络上。
我的问题是:
接收器应用程序本身非常基础。除了许可证包装外,它类似于原始示例应用程序:
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;
}
}
}
答案 0 :(得分:1)
cast.framework.messages.MessageType.LOAD
的处理程序应始终返回:
loadRequestData
,或loadRequestData
的承诺null
放弃加载请求(我不是100%确信这适用于加载请求)如果不执行此操作,则装入请求将保留在队列中,并且任何新请求都将在初始请求之后排队。
在处理程序中,如果!loadRequestData.media
返回错误,则将使您进入该状态。另一种可能性是加载请求处理程序中的异常,这也会使您进入该状态。
答案 1 :(得分:0)
我猜我们有一种不同的方法,可以通过sendMessage
发送所有可能的东西,当我们加载内容时,我们创建了一个new cast.framework.messages.LoadRequestData()
,并通过playerManager.load(loadRequest)
进行了加载。
但是我想您可能正在集成的Chromecast上进行测试,我们也看到了这个问题!?
我建议您做一个或多个
playerManager.stop()
(也许在隔行间吗?)licenseUrl
的设置方式我们如何设置licenseUrl
playerManager.setMediaPlaybackInfoHandler((loadRequestData, playbackConfig) => {
playbackConfig.licenseUrl = loadRequestData.customData.licenseUrl;
return playbackConfig;
}
);