Object type webkitMediaStream lost when using sendMessage in chrome extension

So I'm trying to grab web audio from a tab and pipe it to another script that works with DOM elements on the page.

SCRIPT EXTENSION

In background.js

I am using the following script:

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

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

      

The Developer Toolkit shows me that the created object is indeed a MediaStream object. (Which is what I want and seems to be working fine).

EXPANSION DESIGN:

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

SCRIPT CONTENTS

I am using the content script (injected) in the page itself to pull out the serialized JSON object:

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

      

CONSOLE PAGE

Unfortunately, due to JSON serialization, the object type is lost:

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

I need a recast object as a MediaStream object and tried the following:

Attempt 1: FAILED

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

      

Attempt 2: FAILED

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

      

Attempt 3: FAILED

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

      

Note: Constructor for MediaStream

IS object webkitMediaStream

.

I need either a better method to pass an object from the script extension (the only place the method works from chrome.tab.capture()

) to the content script (the only place that can access and modify the DOM elements of the page)

OR

I need a way to translate a serialized JSON object back into a fully functional object MediaStream

.

Thanks in advance!

JRad the Bad

+3


source to share


1 answer


The internal messages are always JSON serialized, so it's really obvious that you can't post MediaStream

from a man page to a web page. The question is, do you really need to send MediaStream

from background to content script?

  • If you only need to display the video, for example, then you can use URL.createObjectURL

    to get blob:

    -URL for the stream and assign it video.src

    to see the video. The url generated URL.createObjectURL

    can only be used by a page of the same origin, so you need to create a tag <video>

    on the page chrome-extension://

    ; either in the tab or in the frame. If you want to do this in a frame, make sure the page is listed in web_accessible_resources

    .

If you really want a MediaStream

tab tab object , then RTCPeerConnection

you can use to send a stream.This WebRTC API is usually used to exchange media streams between peers on the network, but it can also be used to send streams from one page to another page in another tab or in a browser.

Here's a complete example. Visit any web page and click the extension button. The extension will then add the video to the page with the current tab.

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

      

+2


source







All Articles