【问题标题】:Ways to capture incoming WebRTC video streams (client side)捕获传入 WebRTC 视频流的方法(客户端)
【发布时间】:2018-06-18 14:21:54
【问题描述】:

我目前正在寻找一种存储传入 webrtc 视频流的最佳方法。我正在使用 webrtc(通过 chrome)加入视频通话,我想记录从每个参与者到浏览器的每个传入视频流。 我正在研究的解决方案是:

  • 拦截进入浏览器的网络数据包,例如使用 Whireshark 然后解码。关注本文:https://webrtchacks.com/video_replay/

  • 修改浏览器以将录音存储为文件,例如通过修改 Chromium 本身

由于资源限制,任何屏幕录像机或使用 xvfb 和 ffmpeg 等解决方案都不是一个选项。有没有其他方法可以让我将数据包或编码视频捕获为文件?该解决方案必须在 Linux 上运行。

【问题讨论】:

    标签: google-chrome webrtc chromium openwebrtc


    【解决方案1】:

    如果媒体流是你想要的一个方法是覆盖浏览器的PeerConnection。这是一个例子:

    在扩展清单中添加以下内容脚本:

    content_scripts": [
        {
          "matches": ["http://*/*", "https://*/*"],
          "js": ["payload/inject.js"],
          "all_frames": true,
          "match_about_blank": true,
          "run_at": "document_start"
        }
    ]
    

    注入.js

    var inject = '('+function() { 
        //overide the browser's default RTCPeerConnection. 
        var origPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
        //make sure it is supported
        if (origPeerConnection) {
    
            //our own RTCPeerConnection
            var newPeerConnection = function(config, constraints) {
                console.log('PeerConnection created with config', config);
                //proxy the orginal peer connection
                var pc = new origPeerConnection(config, constraints);
                //store the old addStream
                var oldAddStream = pc.addStream;
    
                //addStream is called when a local stream is added. 
                //arguments[0] is a local media stream
                pc.addStream = function() {
                    console.log("our add stream called!")
                    //our mediaStream object
                    console.dir(arguments[0])
                    return oldAddStream.apply(this, arguments);
                }
    
                //ontrack is called when a remote track is added.
                //the media stream(s) are located in event.streams
                pc.ontrack = function(event) {
                    console.log("ontrack got a track")
                    console.dir(event);
                }
    
                window.ourPC = pc;
    
                return pc; 
            };
    
        ['RTCPeerConnection', 'webkitRTCPeerConnection', 'mozRTCPeerConnection'].forEach(function(obj) {
            // Override objects if they exist in the window object
            if (window.hasOwnProperty(obj)) {
                window[obj] = newPeerConnection;
                // Copy the static methods
                Object.keys(origPeerConnection).forEach(function(x){
                    window[obj][x] = origPeerConnection[x];
                })
                window[obj].prototype = origPeerConnection.prototype;
            }
        });
      }
    
    }+')();';
    var script = document.createElement('script');
    script.textContent = inject;
    (document.head||document.documentElement).appendChild(script);
    script.parentNode.removeChild(script);
    

    我在 google hangouts 中通过语音通话对此进行了测试,发现两个 mediaStream 是通过 pc.addStream 添加的,一个轨道是通过 pc.ontrack 添加的。 addStream 似乎是本地媒体流,ontrack 中的事件对象是具有流对象的 RTCTrackEvent。我认为这是您正在寻找的。​​p>

    要从扩展的内容脚本访问这些流,您需要创建音频元素并将“srcObject”属性设置为媒体流:例如

    pc.ontrack = function(event) {
    
        //check if our element exists
        var elm = document.getElementById("remoteStream");
        if(elm == null) {
            //create an audio element
            elm = document.createElement("audio");
            elm.id = "remoteStream";
    
        }
    
        //set the srcObject to our stream. not sure if you need to clone it
        elm.srcObject = event.streams[0].clone();
        //write the elment to the body
        document.body.appendChild(elm);
    
        //fire a custom event so our content script knows the stream is available.
        // you could pass the id in the "detail" object. for example:
        //CustomEvent("remoteStreamAdded", {"detail":{"id":"audio_element_id"}})
        //then access if via e.detail.id in your event listener.
        var e = CustomEvent("remoteStreamAdded");
        window.dispatchEvent(e);
    
    }
    

    然后在您的内容脚本中,您可以像这样监听该事件/访问媒体流:

    window.addEventListener("remoteStreamAdded", function(e) {
        elm = document.getElementById("remoteStream");
        var stream = elm.captureStream();
    })
    

    借助内容脚本可用的捕获流,您几乎可以用它做任何您想做的事情。例如,MediaRecorder 非常适合记录流,或者您可以使用 peer.js 或 binary.js 之类的东西来流式传输到另一个源。

    我尚未对此进行测试,但也应该可以覆盖本地流。例如,在 inject.js 中,您可以建立一些空白媒体流,覆盖 navigator.mediaDevices.getUserMedia,而不是返回本地媒体流,而是返回您自己的媒体流。

    假设您使用扩展程序/应用程序在文档开头加载 inject.js 脚本,此方法应该可以在 Firefox 和其他人中使用。在任何目标库之前加载它是完成这项工作的关键。

    已编辑以获取更多详细信息

    已编辑以获取更多细节

    【讨论】:

    • 这个答案还有意义吗?我已经在 Google Meet 中尝试过您的 inject.js 方法,但没有调用 newPeerConnection 函数
    • @nomadcrypto 非常感谢您的回答。如果它在 github 或其他任何地方,你能分享更多的代码吗?
    • @nomadcrypt 你能提供更新吗?现在使用的大多数方法都已弃用
    【解决方案2】:

    捕获数据包只会为您提供网络数据包,然后您需要将其转换为帧并放入容器中。像Janus 这样的服务器可以录制视频。

    运行 headless chrome 并使用 javascript MediaRecorder API 是另一种选择,但会占用更多资源。

    【讨论】:

    • 据我了解 Janus 所做的,这需要控制 webrtc 服务器。我想将通话记录为外部视频通话的参与者,而无需对服务器进行任何控制。有没有办法以某种方式代理例如我从浏览器实例到 Janus 的本地流量?
    • 如果您有一个使用 MediaRecorder API 的浏览器实例,请参阅 here 以获取示例。您需要使用 Chrome 的 tabcapture 来捕获标签并为本地和远程参与者添加音频。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-26
    • 1970-01-01
    • 2011-06-23
    相关资源
    最近更新 更多