0

背景
我正在构建一个局域网,带有树莓派相机模块和 USB 麦克风的 WebRTC 婴儿监视器。流是用 GStreamer 合成的,我使用 Janus Gateway 来促进 Web 浏览器和 Pi 之间的 WebRTC 连接。网页和 Javascript 是Meetecho 提供的流媒体演示的精简版。

这是我第一次使用这些技术中的许多,目前我对故障排除有点不知所措。我无法弄清楚为什么该网页在 Chrome 和 Safari 中有效,但在 Firefox 中无效。

它在 Chrome 和 Safari 上运行良好: Chrome 中的应用程序

在 Firefox 中,WebRTC 连接似乎已成功建立和维护(基于 Chrome 和 Firefox 之间的网络流量和控制台输出的比较),但页面似乎在此过程中的某个地方被赶上了: 它在Firefox中不起作用

比较控制台在比较ChromeFirefox
的控制台输出时,它们是相同的,直到此时两个控制台都报告但可能出于不同的原因?Uncaught (in promise) DOMException:

  • 火狐说Uncaught (in promise) DOMException: The fetching process for the media resource was aborted by the user agent at the user's request.
  • 铬 说Uncaught (in promise) DOMException: The play() request was interrupted by a new load request.

这些是具有不同“提示”的相同错误吗?还是由于浏览器之间的一些潜在差异,它们实际上是不同的错误?

在此处输入图像描述

在此错误之后,Firefox 立即通过报告Remote track removed.

我不确定我是否在 JS 中做了一些愚蠢的事情来导致这种情况,或者我是否缺少关于 Firefox 的一些细微差别。

其他可能有用的细节?
下面是页面的html (index.html)javascript (janus_stream.js)的一部分,pastebin 链接包含整个 janus_stream.js。

// We make use of this 'server' variable to provide the address of the
// REST Janus API. By default, in this example we assume that Janus is
// co-located with the web server hosting the HTML pages but listening
// on a different port (8088, the default for HTTP in Janus), which is
// why we make use of the 'window.location.hostname' base address. Since
// Janus can also do HTTPS, and considering we don't really want to make
// use of HTTP for Janus if your demos are served on HTTPS, we also rely
// on the 'window.location.protocol' prefix to build the variable, in
// particular to also change the port used to contact Janus (8088 for
// HTTP and 8089 for HTTPS, if enabled).
// In case you place Janus behind an Apache frontend (as we did on the
// online demos at http://janus.conf.meetecho.com) you can just use a
// relative path for the variable, e.g.:
//
//      var server = "/janus";
//
// which will take care of this on its own.
//
//
// If you want to use the WebSockets frontend to Janus, instead, you'll
// have to pass a different kind of address, e.g.:
//
//      var server = "ws://" + window.location.hostname + ":8188";
//
// Of course this assumes that support for WebSockets has been built in
// when compiling the server. WebSockets support has not been tested
// as much as the REST API, so handle with care!
//
//
// If you have multiple options available, and want to let the library
// autodetect the best way to contact your server (or pool of servers),
// you can also pass an array of servers, e.g., to provide alternative
// means of access (e.g., try WebSockets first and, if that fails, fall
// back to plain HTTP) or just have failover servers:
//
//      var server = [
//          "ws://" + window.location.hostname + ":8188",
//          "/janus"
//      ];
//
// This will tell the library to try connecting to each of the servers
// in the presented order. The first working server will be used for
// the whole session.
//
var server = null;
if(window.location.protocol === 'http:')
    server = "http://" + window.location.hostname + ":8088/janus";
else
    server = "https://" + window.location.hostname + ":8089/janus";

var janus = null;
var streaming = null;
var opaqueId = "streamingtest-"+Janus.randomString(12);

var bitrateTimer = null;
var spinner = true;

var simulcastStarted = false, svcStarted = false;

var selectedStream = null;


$(document).ready(function() {
    // Initialize the library (all console debuggers enabled)
    Janus.init({debug: "all", callback: function() {
        // Use a button to start the demo
        //$('#start').one('click', function() {
            //$(this).attr('disabled', true).unbind('click');
            // Make sure the browser supports WebRTC
            if(!Janus.isWebrtcSupported()) {
                bootbox.alert("No WebRTC support... ");
                return;
            }
            // Create session
            janus = new Janus(
                {
                    server: server,
                    success: function() {
                        // Attach to Streaming plugin
                        janus.attach(
                            {
                                plugin: "janus.plugin.streaming",
                                opaqueId: opaqueId,
                                success: function(pluginHandle) {
                                    $('#details').remove();
                                    streaming = pluginHandle;
                                    Janus.log("Plugin attached! (" + streaming.getPlugin() + ", id=" + streaming.getId() + ")");
                                    // Setup streaming session
                                    $('#update-streams').click(updateStreamsList);
                                    updateStreamsList();
                                    $('#start').removeAttr('disabled').html("Stop")
                                        .click(function() {
                                            $(this).attr('disabled', true);
                                            clearInterval(bitrateTimer);
                                            janus.destroy();
                                            $('#streamslist').attr('disabled', true);
                                            $('#watch').attr('disabled', true).unbind('click');
                                            $('#start').attr('disabled', true).html("Bye").unbind('click');
                                        });
                                },
                                error: function(error) {
                                    Janus.error("  -- Error attaching plugin... ", error);
                                    bootbox.alert("Error attaching plugin... " + error);
                                },
                                iceState: function(state) {
                                    Janus.log("ICE state changed to " + state);
                                },
                                webrtcState: function(on) {
                                    Janus.log("Janus says our WebRTC PeerConnection is " + (on ? "up" : "down") + " now");
                                },
                                onmessage: function(msg, jsep) {
                                    Janus.debug(" ::: Got a message :::", msg);
                                    var result = msg["result"];
                                    if(result) {
                                        if(result["status"]) {
                                            var status = result["status"];
                                            if(status === 'starting')
                                                $('#status').removeClass('hide').text("Starting, please wait...").show();
                                            else if(status === 'started')
                                                $('#status').removeClass('hide').text("Started").show();
                                            else if(status === 'stopped')
                                                stopStream();
                                        } else if(msg["streaming"] === "event") {
                                            // Is simulcast in place?
                                            var substream = result["substream"];
                                            var temporal = result["temporal"];
                                            if((substream !== null && substream !== undefined) || (temporal !== null && temporal !== undefined)) {
                                                if(!simulcastStarted) {
                                                    simulcastStarted = true;
                                                    addSimulcastButtons(temporal !== null && temporal !== undefined);
                                                }
                                                // We just received notice that there's been a switch, update the buttons
                                                updateSimulcastButtons(substream, temporal);
                                            }
                                            // Is VP9/SVC in place?
                                            var spatial = result["spatial_layer"];
                                            temporal = result["temporal_layer"];
                                            if((spatial !== null && spatial !== undefined) || (temporal !== null && temporal !== undefined)) {
                                                if(!svcStarted) {
                                                    svcStarted = true;
                                                    addSvcButtons();
                                                }
                                                // We just received notice that there's been a switch, update the buttons
                                                updateSvcButtons(spatial, temporal);
                                            }
                                        }
                                    } else if(msg["error"]) {
                                        bootbox.alert(msg["error"]);
                                        stopStream();
                                        return;
                                    }
                                    if(jsep) {
                                        Janus.debug("Handling SDP as well...", jsep);
                                        var stereo = (jsep.sdp.indexOf("stereo=1") !== -1);
                                        // Offer from the plugin, let's answer
                                        streaming.createAnswer(
                                            {
                                                jsep: jsep,
                                                // We want recvonly audio/video and, if negotiated, datachannels
                                                media: { audioSend: false, videoSend: false, data: true },
                                                customizeSdp: function(jsep) {
                                                    if(stereo && jsep.sdp.indexOf("stereo=1") == -1) {
                                                        // Make sure that our offer contains stereo too
                                                        jsep.sdp = jsep.sdp.replace("useinbandfec=1", "useinbandfec=1;stereo=1");
                                                    }
                                                },
                                                success: function(jsep) {
                                                    Janus.debug("Got SDP!", jsep);
                                                    var body = { request: "start" };
                                                    streaming.send({ message: body, jsep: jsep });
                                                    $('#watch').html("Stop").removeAttr('disabled').click(stopStream);
                                                },
                                                error: function(error) {
                                                    Janus.error("WebRTC error:", error);
                                                    bootbox.alert("WebRTC error... " + error.message);
                                                }
                                            });
                                    }
                                },
                                onremotestream: function(stream) {
                                    Janus.debug(" ::: Got a remote stream :::", stream);
                                    var addButtons = false;
                                    if($('#remotevideo').length === 1) {
                                        addButtons = true;
                                        //$('#stream').append('<video class="rounded centered hide" id="remotevideo" width="100%" height="100%" playsinline/>');
                                        $('#remotevideo').get(0).volume = 0;
                                        // Show the stream and hide the spinner when we get a playing event
                                        $("#remotevideo").bind("playing", function () {
                                            $('#waitingvideo').remove();
                                            if(this.videoWidth)
                                                $('#remotevideo').removeClass('hide').show();
                                            if(spinner)
                                                spinner.stop();
                                            spinner = null;
                                            var videoTracks = stream.getVideoTracks();
                                            if(!videoTracks || videoTracks.length === 0)
                                                return;
                                            var width = this.videoWidth;
                                            var height = this.videoHeight;
                                            $('#curres').removeClass('hide').text(width+'x'+height).show();
                                            if(Janus.webRTCAdapter.browserDetails.browser === "firefox") {
                                                // Firefox Stable has a bug: width and height are not immediately available after a playing
                                                setTimeout(function() {
                                                    var width = $("#remotevideo").get(0).videoWidth;
                                                    var height = $("#remotevideo").get(0).videoHeight;
                                                    $('#curres').removeClass('hide').text(width+'x'+height).show();
                                                }, 2000);
                                            }
                                        });
                                    }
                                    Janus.attachMediaStream($('#remotevideo').get(0), stream);
                                    $("#remotevideo").get(0).play();
                                    $("#remotevideo").get(0).volume = 1;
                                    var videoTracks = stream.getVideoTracks();
                                    if(!videoTracks || videoTracks.length === 0) {
                                        // No remote video
                                        $('#remotevideo').hide();
                                        if($('#stream .no-video-container').length === 0) {
                                            $('#stream').append(
                                                '<div class="no-video-container">' +
                                                    '<i class="fa fa-video-camera fa-5 no-video-icon"></i>' +
                                                    '<span class="no-video-text">No remote video available</span>' +
                                                '</div>');
                                        }
                                    } else {
                                        $('#stream .no-video-container').remove();
                                        $('#remotevideo').removeClass('hide').show();
                                    }
                                    if(!addButtons)
                                        return;
                                    if(videoTracks && videoTracks.length &&
                                            (Janus.webRTCAdapter.browserDetails.browser === "chrome" ||
                                                Janus.webRTCAdapter.browserDetails.browser === "firefox" ||
                                                Janus.webRTCAdapter.browserDetails.browser === "safari")) {
                                        $('#curbitrate').removeClass('hide').show();
                                        bitrateTimer = setInterval(function() {
                                            // Display updated bitrate, if supported
                                            var bitrate = streaming.getBitrate();
                                            $('#curbitrate').text(bitrate);
                                            // Check if the resolution changed too
                                            var width = $("#remotevideo").get(0).videoWidth;
                                            var height = $("#remotevideo").get(0).videoHeight;
                                            if(width > 0 && height > 0)
                                                $('#curres').removeClass('hide').text(width+'x'+height).show();
                                        }, 1000);
                                    }
                                },
                                ondataopen: function(data) {
                                    Janus.log("The DataChannel is available!");
                                    $('#waitingvideo').remove();
                                    $('#stream').append(
                                        '<input class="form-control" type="text" id="datarecv" disabled></input>'
                                    );
                                    if(spinner)
                                        spinner.stop();
                                    spinner = null;
                                },
                                ondata: function(data) {
                                    Janus.debug("We got data from the DataChannel!", data);
                                    $('#datarecv').val(data);
                                },
                                oncleanup: function() {
                                    Janus.log(" ::: Got a cleanup notification :::");
                                    $('#waitingvideo').remove();
                                    $('#remotevideo').remove();
                                    $('#datarecv').remove();
                                    $('.no-video-container').remove();
                                    $('#bitrate').attr('disabled', true);
                                    $('#bitrateset').html('Bandwidth<span class="caret"></span>');
                                    $('#curbitrate').hide();
                                    if(bitrateTimer)
                                        clearInterval(bitrateTimer);
                                    bitrateTimer = null;
                                    $('#curres').hide();
                                    $('#simulcast').remove();
                                    $('#metadata').empty();
                                    $('#info').addClass('hide').hide();
                                    simulcastStarted = false;
                                }
                            });
                    },
                    error: function(error) {
                        Janus.error(error);
                        bootbox.alert(error, function() {
                            window.location.reload();
                        });
                    },
                    destroyed: function() {
                        window.location.reload();
                    }
                });
        //});
    }});
});

function updateStreamsList() {
    $('#update-streams').unbind('click').addClass('fa-spin');
    var body = { request: "list" };
    Janus.debug("Sending message:", body);
    streaming.send({ message: body, success: function(result) {
        setTimeout(function() {
            $('#update-streams').removeClass('fa-spin').click(updateStreamsList);
        }, 500);
        if(!result) {
            bootbox.alert("Got no response to our query for available streams");
            return;
        }
        if(result["list"]) {
            $('#streams').removeClass('hide').show();
            $('#streamslist').empty();
            $('#watch').attr('disabled', true).unbind('click');
            var list = result["list"];
            Janus.log("Got a list of available streams");
            if(list && Array.isArray(list)) {
                list.sort(function(a, b) {
                    if(!a || a.id < (b ? b.id : 0))
                        return -1;
                    if(!b || b.id < (a ? a.id : 0))
                        return 1;
                    return 0;
                });
            }
            Janus.debug(list);
            for(var mp in list) {
                Janus.debug("  >> [" + list[mp]["id"] + "] " + list[mp]["description"] + " (" + list[mp]["type"] + ")");
                $('#streamslist').append("<li><a href='#' id='" + list[mp]["id"] + "'>" + list[mp]["description"] + " (" + list[mp]["type"] + ")" + "</a></li>");
            }
            $('#streamslist a').unbind('click').click(function() {
                selectedStream = $(this).attr("id");
                $('#streamset').html($(this).html()).parent().removeClass('open');
                return false;

            });
            $('#watch').removeAttr('disabled').unbind('click').click(startStream);
        }
    }});
}

function getStreamInfo() {
    $('#metadata').empty();
    $('#info').addClass('hide').hide();
    if(!selectedStream)
        return;
    // Send a request for more info on the mountpoint we subscribed to
    var body = { request: "info", id: parseInt(selectedStream) || selectedStream };
    streaming.send({ message: body, success: function(result) {
        if(result && result.info && result.info.metadata) {
            $('#metadata').html(result.info.metadata);
            $('#info').removeClass('hide').show();
        }
    }});
}

function startStream() {
    selectedStream = "1"
    Janus.log("Selected video id #" + selectedStream);
    if(!selectedStream) {
        bootbox.alert("Select a stream from the list");
        return;
    }
    $('#streamset').attr('disabled', true);
    $('#streamslist').attr('disabled', true);
    $('#watch').attr('disabled', true).unbind('click');
    var body = { request: "watch", id: parseInt(selectedStream) || selectedStream};
    streaming.send({ message: body });
    // No remote video yet
    $('#stream').append('<video class="rounded centered" id="waitingvideo" width="100%" height="100%" />');
    if(spinner == null) {
        var target = document.getElementById('stream');
        spinner = new Spinner({top:100}).spin(target);
    } else {
        spinner.spin();
    }
    // Get some more info for the mountpoint to display, if any
    getStreamInfo();
}

function stopStream() {
    $('#watch').attr('disabled', true).unbind('click');
    var body = { request: "stop" };
    streaming.send({ message: body });
    streaming.hangup();
    $('#streamset').removeAttr('disabled');
    $('#streamslist').removeAttr('disabled');
    $('#watch').html("Watch or Listen").removeAttr('disabled').unbind('click').click(startStream);
    $('#status').empty().hide();
    $('#bitrate').attr('disabled', true);
    $('#bitrateset').html('Bandwidth<span class="caret"></span>');
    $('#curbitrate').hide();
    if(bitrateTimer)
        clearInterval(bitrateTimer);
    bitrateTimer = null;
    $('#curres').empty().hide();
    $('#simulcast').remove();
    simulcastStarted = false;
}

.......
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>BabyPi Cam</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/webrtc-adapter/7.4.0/adapter.min.js" ></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js" ></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/5.4.0/bootbox.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/spin.js/2.3.2/spin.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.4/toastr.min.js"></script>
<script type="text/javascript" src="janus.js" ></script>
<script type="text/javascript" src="janus_stream.js"></script>
<script>
    $(function() {
        $(".navbar-static-top").load("navbar.html", function() {
            $(".navbar-static-top li.dropdown").addClass("active");
            $(".navbar-static-top a[href='streamingtest.html']").parent().addClass("active");
        });
        $(".footer").load("footer.html");
    });
</script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/3.4.0/cerulean/bootstrap.min.css" type="text/css"/>
<link rel="stylesheet" href="css/demo.css" type="text/css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" type="text/css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.4/toastr.min.css"/>
</head>
<body>

<div class="container">         
    <div class="col-md-12">
        <div class="panel panel-default">
            <div class="panel-heading">
                <h3 class="panel-title">BabyPi Live Stream
                    <span class="label label-info" id="status"></span>
                    <span class="label label-primary" id="curres"></span>
                    <span class="label label-info" id="curbitrate" styple="display:inline;"></span>
                </h3>
            </div>
            <div class="panel-body" id="stream">
                <video class="rounded centered" id="remotevideo" width="100%" height="100%" playsinline controls muted></video>
            </div>
        </div>
    </div>
</div>

</body>
</html>

我也在使用提供的janus.js API Meetecho

问题

  • 我做错了什么阻止它在 Firefox 中工作?
  • 不同的控制台输出可以告诉我我做错了什么?
  • 您对在哪里查看/尝试什么以使其在 Firefox 中工作有任何建议吗?

非常感谢任何指针或想法!如果我可以提供其他信息,请告诉我。

谢谢!

更新:理论/可能的答案?
为了解决该Uncaught (in promise) DOMException: The fetching process for the media resource was aborted by the user agent at the user's request.错误,我将 video.play() 更改为 video.load()。这解决了错误,但相同的Remote track removed“无远程视频”行为仍然存在。

与此同时,我可能发现了更根本的问题:Pi 的视频流是 H264,据我所知,Firefox 不支持这种格式?也许这就是我在使用 Firefox 时遇到问题的原因?

你们中的任何人都可以确认或否认这是真正的问题吗?

4

1 回答 1

0

该问题H264 不兼容有关,但在看到此线程后,我意识到我是同一问题的受害者。

我需要更新janus.plugin.streaming.jcfg文件中的一行,使其看起来像这样:

RPI3: {  
    type = "rtp"  
    id = 1
    description = "Raspberry Pi 3 Infrared Camera Module stream"  
    video = true  
    videoport = 5001  
    videopt = 96  
    videortpmap = "H264/90000"  
    videofmtp = "profile-level-id=42e01f;packetization-mode=1"  
    audio = true  
    audioport = 5002  
    audiopt = 111  
    audiortpmap = "opus/48000/2" 
}

以前我使用的是导致问题的“不完整”行:

    ...
    videofmtp = "packetization-mode=1"
    ...

显然,这启用了可以与 Firefox 的 OpenH264 插件一起使用的正确 H264“配置文件”。现在我可以使用 chrome 和 firefox 查看流了!

于 2021-06-25T21:07:17.230 回答