在 Chrome 扩展中使用 sendMessage 时丢失了 webkitMediaStream 对象类型

JRa*_*Bad 6 javascript json casting sendmessage google-chrome-extension

所以我试图从选项卡中捕获网络音频并将其传递到另一个与页面上的 DOM 元素一起使用的脚本中。

扩展脚本

在 中background.js,我使用以下脚本:

    chrome.tabCapture.capture(constraints, function(stream) {
        console.log("\ngot stream");
        console.log(stream);

        chrome.tabs.sendMessage(tabID, {
            "message": "stream",
            "stream": stream
        });
    });
Run Code Online (Sandbox Code Playgroud)

Developer Toolkit 向我展示了创建的对象确实是一个 MediaStream 对象。(我想要并且似乎工作正常)。

扩展控制台:

MediaStream {onremovetrack: null, onaddtrack: null, onended: null, ended: false, id: "c0jm4lYJus3XCwQgesUGT9lpyPQiWlGKHb7q"…}

内容脚本

我在页面本身上使用内容脚本(注入),然后将 JSON 序列化对象拉回:

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
  if (request.message === "stream") {
    var thisStream = request.stream;
    console.log(thisStream);
    if (!thisStream) {
      console.log("stream is null");
      return;
    }    
    loadStream(thisStream);
  }
  else if (request.message === "statusChanged") {
    console.log("statusChanged");
  }
});
Run Code Online (Sandbox Code Playgroud)

页面控制台

不幸的是,由于 JSON 序列化,对象类型丢失了:

Object {onremovetrack: null, onaddtrack: null, onended: null, ended: false, id: "c0jm4lYJus3XCwQgesUGT9lpyPQiWlGKHb7q"…}

我需要将对象重铸为 MediaStream 对象,并尝试了以下所有失败的事情:

尝试 1:失败

var stream = new webkitMediaStream;
function loadStream(thisStream) {
    stream = thisStream;
}
Run Code Online (Sandbox Code Playgroud)

尝试 2:失败

var stream;
function loadStream(thisStream) {
    stream = new webkitMediaStream(thisStream);
}
Run Code Online (Sandbox Code Playgroud)

尝试 3:失败

var stream;
function loadStream(thisStream) {
    stream = Object.create(webkitMediaStream, thisStream);
}
Run Code Online (Sandbox Code Playgroud)

注意:MediaStream对象IS 的构造函数webkitMediaStream

我需要一个更好的方法来将对象从扩展脚本(该chrome.tab.capture()方法工作的唯一地方)传递到内容脚本(唯一可以访问并可以修改页面的 DOM 元素的地方),

或者

我需要一种将 JSON 序列化对象重新转换为功能齐全的MediaStream对象的方法。

提前致谢!

坏人

Rob*_*b W 7

扩展消息总是 JSON 序列化的,所以很明显你不能MediaStream从后台页面发送 a到网页。问题是,你真的需要MediaStream从后台发送到内容脚本吗?

  • 如果您只需要,例如显示视频,那么您可以使用URL.createObjectURL获取blob:流的-URL 并将其分配video.src给观看视频。创建的 URLURL.createObjectURL只能被同一来源的页面使用,所以需要<video>chrome-extension://页面中创建标签;要么在选项卡中,要么在框架中。如果要在框架中执行此操作,请确保该页面列在 中web_accessible_resources

如果您确实需要MediaStream选项卡中的选项卡对象,则RTCPeerConnection可以用于发送流。此 WebRTC API 通常用于在网络中的对等点之间交换媒体流,但它也可用于将流从一个页面发送到另一个选项卡或浏览器中的另一个页面。

这是一个完整的例子。访问任何网页,然后单击扩展按钮。然后扩展程序将在显示当前选项卡的页面中插入一个视频。

背景.js

function sendStreamToTab(tabId, stream) {
    var pc = new webkitRTCPeerConnection({iceServers:[]});
    pc.addStream(stream);
    pc.createOffer(function(offer) {
        pc.setLocalDescription(offer, function() {
            // Use chrome.tabs.connect instead of sendMessage
            // to make sure that the lifetime of the stream
            // is tied to the lifetime of the consumer (tab).
            var port = chrome.tabs.connect(tabId, {name: 'tabCaptureSDP'});
            port.onDisconnect.addListener(function() {
                stopStream(stream);
            });
            port.onMessage.addListener(function(sdp) {
                pc.setRemoteDescription(new RTCSessionDescription(sdp));
            });
            port.postMessage(pc.localDescription);
        });
    });
}

function stopStream(stream) {
    var tracks = this.getTracks();
    for (var i = 0; i < tracks.length; ++i) {
        tracks[i].stop();
    }
}

function captureTab(tabId) {
    // Note: this method must be invoked by the user as defined
    // in https://crbug.com/489258, e.g. chrome.browserAction.onClicked.
    chrome.tabCapture.capture({
        audio: true,
        video: true,
        audioConstraints: {
            mandatory: {
                chromeMediaSource: 'tab',
            },
        },
        videoConstraints: {
            mandatory: {
                chromeMediaSource: 'tab',
            },
        },
    }, function(stream) {
        if (!stream) {
            alert('Stream creation failed: ' + chrome.runtime.lastError.message);
        }
        chrome.tabs.executeScript(tabId, {file: 'contentscript.js'}, function() {
            if (chrome.runtime.lastError) {
                stopStream(stream);
                alert('Script injection failed:' + chrome.runtime.lastError.message);
            } else {
                sendStreamToTab(tabId, stream);
            }
        });
    });
}

chrome.browserAction.onClicked.addListener(function(tab) {
    captureTab(tab.id);
});
Run Code Online (Sandbox Code Playgroud)

内容脚本.js

function onReceiveStream(stream) {
    // Just to show that we can receive streams:
    var video = document.createElement('video');
    video.style.border = '1px solid black';
    video.src = URL.createObjectURL(stream);
    document.body.insertBefore(video, document.body.firstChild);
}

function onReceiveOfferSDP(sdp, sendResponse) {
    var pc = new webkitRTCPeerConnection({iceServers:[]});
    pc.onaddstream = function(event) {
        onReceiveStream(event.stream);
    };
    pc.setRemoteDescription(new RTCSessionDescription(sdp), function() {
        pc.createAnswer(function(answer) {
            pc.setLocalDescription(answer);
            sendResponse(pc.localDescription);
        });
    });
}

// Run once to prevent the message from being handled twice when
// executeScript is called multiple times.
if (!window.hasRun) {
    window.hasRun = 1;
    chrome.runtime.onConnect.addListener(function(port) {
        if (port.name === 'tabCaptureSDP') {
            port.onMessage.addListener(function(remoteDescription) {
                onReceiveOfferSDP(remoteDescription, function(sdp) {
                    port.postMessage(sdp);
                });
            });
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

清单文件

{
    "name": "tabCapture to tab",
    "version": "1",
    "manifest_version": 2,
    "background": {
        "scripts": ["background.js"],
        "persistent": false
    },
    "browser_action": {
        "default_title": "Capture tab"
    },
    "permissions": [
        "activeTab",
        "tabCapture"
    ]
}
Run Code Online (Sandbox Code Playgroud)