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" 
    ] 
}