我正在使用 WebTorrent ( https://webtorrent.io , https://github.com/feross/webtorrent ) 和使用 JavaScript (Chrome) SDK 的 Google Cast 发件人应用程序开发 Google Cast 自定义接收器应用程序。
我的应用程序的想法是从 Google Cast 发送器向 Google Cast 接收器发送 torrent id(类似磁铁 URImagnet:?xt=urn:btih:6a9759bffd5c0af65319979fb7832189f4f3c35d
或 HTTP/HTTPS URL 到 *.torrent 文件https://webtorrent.io/torrents/sintel.torrent
),并在 Google Cast 接收器中使用 WebTorrent 来显示媒体(视频或音频) 来自洪流。
请注意,torrent id 不是媒体文件的直接 URL。
现在我使用 Google Cast 命名空间和 messageBus 来发送和接收 torrent id。
WebTorrent API 提供了两种媒体显示方式:
file.appendTo
使用:https://webtorrent.io/docs#-file-appendto-rootelem-function-callback-err-elem-将其附加到 DOMfile.renderTo
使用:https://webtorrent.io/docs#-file-renderto-elem-function-callback-err-elem-直接渲染到给定的元素(或 CSS 选择器)
这是我的接收器的代码:
<html>
<head>
<script src="https://www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js"></script>
<script src="https://cdn.jsdelivr.net/webtorrent/latest/webtorrent.min.js"></script>
</head>
<body>
<video autoplay id='media' />
<script>
window.mediaElement = document.getElementById('media');
window.mediaManager = new cast.receiver.MediaManager(window.mediaElement);
window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance();
window.messageBus = window.castReceiverManager.getCastMessageBus('urn:x-cast:com.google.cast.sample.helloworld');
window.messageBus.onMessage = function(event) {
displayVideo(event.data);
// Inform all senders on the CastMessageBus of the incoming message event
// sender message listener will be invoked
window.messageBus.send(event.senderId, event.data);
};
function displayVideo(torrentId) {
var client = new WebTorrent();
client.add(torrentId, function (torrent) {
var file = torrent.files[0];
file.renderTo('video');
});
}
window.castReceiverManager.start();
</script>
</body>
</html>
这是我的发件人的代码:
<!--
Copyright (C) 2014 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<head>
<style type="text/css">
html, body, #wrapper {
height:100%;
width: 100%;
margin: 0;
padding: 0;
border: 0;
}
#wrapper td {
vertical-align: middle;
text-align: center;
}
input {
font-family: "Arial", Arial, sans-serif;
font-size: 40px;
font-weight: bold;
}
.border {
border: 2px solid #cccccc;
border-radius: 5px;
}
.border:focus {
outline: none;
border-color: #8ecaed;
box-shadow: 0 0 5px #8ecaed;
}
</style>
<script type="text/javascript" src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js"></script>
<script type="text/javascript">
var applicationID = 'F5304A3D';
var namespace = 'urn:x-cast:com.google.cast.sample.helloworld';
var session = null;
/**
* Call initialization for Cast
*/
if (!chrome.cast || !chrome.cast.isAvailable) {
setTimeout(initializeCastApi, 1000);
}
/**
* initialization
*/
function initializeCastApi() {
var sessionRequest = new chrome.cast.SessionRequest(applicationID);
var apiConfig = new chrome.cast.ApiConfig(sessionRequest,
sessionListener,
receiverListener);
chrome.cast.initialize(apiConfig, onInitSuccess, onError);
};
/**
* initialization success callback
*/
function onInitSuccess() {
appendMessage("onInitSuccess");
}
/**
* initialization error callback
*/
function onError(message) {
appendMessage("onError: "+JSON.stringify(message));
}
/**
* generic success callback
*/
function onSuccess(message) {
appendMessage("onSuccess: "+message);
}
/**
* callback on success for stopping app
*/
function onStopAppSuccess() {
appendMessage('onStopAppSuccess');
}
/**
* session listener during initialization
*/
function sessionListener(e) {
appendMessage('New session ID:' + e.sessionId);
session = e;
session.addUpdateListener(sessionUpdateListener);
session.addMessageListener(namespace, receiverMessage);
}
/**
* listener for session updates
*/
function sessionUpdateListener(isAlive) {
var message = isAlive ? 'Session Updated' : 'Session Removed';
message += ': ' + session.sessionId;
appendMessage(message);
if (!isAlive) {
session = null;
}
};
/**
* utility function to log messages from the receiver
* @param {string} namespace The namespace of the message
* @param {string} message A message string
*/
function receiverMessage(namespace, message) {
appendMessage("receiverMessage: "+namespace+", "+message);
};
/**
* receiver listener during initialization
*/
function receiverListener(e) {
if( e === 'available' ) {
appendMessage("receiver found");
}
else {
appendMessage("receiver list empty");
}
}
/**
* stop app/session
*/
function stopApp() {
session.stop(onStopAppSuccess, onError);
}
/**
* send a message to the receiver using the custom namespace
* receiver CastMessageBus message handler will be invoked
* @param {string} message A message string
*/
function sendMessage(message) {
if (session!=null) {
session.sendMessage(namespace, message, onSuccess.bind(this, "Message sent: " + message), onError);
}
else {
chrome.cast.requestSession(function(e) {
session = e;
session.sendMessage(namespace, message, onSuccess.bind(this, "Message sent: " + message), onError);
}, onError);
}
}
/**
* append message to debug message window
* @param {string} message A message string
*/
function appendMessage(message) {
console.log(message);
var dw = document.getElementById("debugmessage");
dw.innerHTML += '\n' + JSON.stringify(message);
};
/**
* utility function to handle text typed in by user in the input field
*/
function update() {
sendMessage(document.getElementById("input").value);
}
/**
* handler for the transcribed text from the speech input
* @param {string} words A transcibed speech string
*/
function transcribe(words) {
sendMessage(words);
}
</script>
</head>
<body>
<table id="wrapper">
<tr>
<td>
<form method="get" action="JavaScript:update();">
<input id="input" class="border" type="text" size="30" onwebkitspeechchange="transcribe(this.value)" x-webkit-speech/>
</form>
</td>
</tr>
</table>
<!-- Debbugging output -->
<div style="margin:10px; visibility:hidden;">
<textarea rows="20" cols="70" id="debugmessage">
</textarea>
</div>
<script type="text/javascript">
document.getElementById("input").focus();
</script>
</body>
</html>
问题:接收方按预期处理来自发送方的种子 ID 和视频播放。但是官方的 Google Cast 应用程序或 Chrome 的官方 Google Cast 扩展程序没有显示用于播放视频以暂停、停止、搜索等的标准媒体控件。
这就是我所拥有的(这是最新版 Google Chrome 中 Google Cast 的标准内置模式对话框的屏幕截图):
这就是我想要实现的(这是最新版 Google Chrome 中 Google Cast 的标准内置模态对话框的屏幕截图):
添加
window.mediaElement = document.getElementById('media');
window.mediaManager = new cast.receiver.MediaManager(window.mediaElement);
为了
<video autoplay id='media' />
元素没有帮助。
我是否应该向发送者和/或接收者添加一些内容以为<video autoplay id='media' />
所有发送者添加标准媒体控件?
也许还有另一种方法可以在不使用 Google Cast 命名空间和 messageBus 的情况下发送和接收 torrent id?
UPD
看来我找到了问题的根源...
如何为接收器中现有的播放视频启用默认媒体控件?
例如,接收方应用程序已经有播放视频:
<video autoplay id='media'
src='https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'
/>
如何启用默认媒体控件 - 此播放视频的工作按钮“播放/暂停”、工作进度条(在所有发件人上,如 Chrome 的官方 Google Cast 扩展程序)?
看起来添加以下代码无济于事:
window.mediaElement = document.getElementById('media');
window.mediaManager = new cast.receiver.MediaManager(window.mediaElement);
window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance();
window.castReceiverManager.start();
这是接收器的完整源代码:
<html>
<head>
<script src="https://www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js"></script>
</head>
<body>
<video autoplay id='media'
src='https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'
/>
<script>
window.mediaElement = document.getElementById('media');
window.mediaManager = new cast.receiver.MediaManager(window.mediaElement);
window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance();
window.castReceiverManager.start();
</script>
</body>
</html>
UPD2:
看起来可以使用任何文本字符串(在我的情况下为 torrent id)而不是媒体 URL,chrome.cast.media.MediaInfo
并使用媒体名称空间而不是使用自定义名称空间和自定义消息总线(即不使用https://developers.google.com /cast/docs/reference/receiver/cast.receiver.CastReceiverManager#getCastMessageBus和https://developers.google.com/cast/docs/reference/receiver/cast.receiver.CastMessageBus和https://developers.google.com /cast/docs/reference/chrome/chrome.cast.Session#sendMessage):
function cast() {
url = 'magnet:?xt=urn:btih:6a9759bffd5c0af65319979fb7832189f4f3c35d';
chrome.cast.requestSession(function(session) {
var mediaInfo = new chrome.cast.media.MediaInfo(url);
//mediaInfo.contentType = 'video/mp4';
//mediaInfo.contentType = 'audio/mpeg';
//mediaInfo.contentType = 'image/jpeg';
var request = new chrome.cast.media.LoadRequest(mediaInfo);
request.autoplay = true;
session.loadMedia(request, function() {}, onError);
}, onError);
}
但是在这种情况下如何在接收器上处理它呢?