3

所以我试图从一个标签捕获网络音频并将其传递到另一个脚本,该脚本与页面上的DOM元素一起工作。webkitMediaStream在Chrome扩展中使用sendMessage时丢失的对象类型

扩展脚本

background.js,我使用下面的脚本:

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

     chrome.tabs.sendMessage(tabID, { 
      "message": "stream", 
      "stream": stream 
     }); 
    }); 

的开发者工具包让我发现,所创建的对象确实是一个MediaStream对象。 (我想要的,似乎工作正常)。

延长CONSOLE:

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"); 
    } 
}); 

PAGE CONSOLE

不幸的是,因为JSON序列化,对象类型丢失:

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

我需要重新塑造成一个MediaStream对象的对象,并尝试下面的东西都失败了:

尝试1:FAILED

var stream = new webkitMediaStream; 
function loadStream(thisStream) { 
    stream = thisStream; 
} 

尝试2:失败

var stream; 
function loadStream(thisStream) { 
    stream = new webkitMediaStream(thisStream); 
} 

尝试3:FAILED

var stream; 
function loadStream(thisStream) { 
    stream = Object.create(webkitMediaStream, thisStream); 
} 

注:MediaStream对象构造ISwebkitMediaStream

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

OR

我需要重铸JSON序列化对象返回到一个功能齐全的MediaStream对象的一种方式。

在此先感谢!

JRad坏

回答

2

扩展信息始终是JSON序列化,所以它确实很明显,你不能从后台页面发送MediaStream的网页。问题是,你真的需要从背景发送MediaStream到内容脚本吗?

  • 如果您只需要显示视频,然后您可以使用URL.createObjectURL获取流的blob: -URL并将其分配到video.src以查看视频。由URL.createObjectURL创建的网址只能由相同来源的网页使用,因此您需要在chrome-extension://页面创建<video>标记;无论是在标签中还是在框架中。如果您想在一个框架中执行此操作,请确保该页面在web_accessible_resources中列出。

如果你真的需要在选项卡的标签的MediaStream对象,然后RTCPeerConnection可用于发送数据流。此WebRTC API通常用于在网络中的对等设备之间交换媒体流,但它也可用于将流从一个页面发送到另一个标签页或浏览器中的另一个页面。

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

background.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); 
}); 

contentscript.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); 
       }); 
      }); 
     } 
    }); 
} 

的manifest.json

{ 
    "name": "tabCapture to tab", 
    "version": "1", 
    "manifest_version": 2, 
    "background": { 
     "scripts": ["background.js"], 
     "persistent": false 
    }, 
    "browser_action": { 
     "default_title": "Capture tab" 
    }, 
    "permissions": [ 
     "activeTab", 
     "tabCapture" 
    ] 
}