奇怪的WebRTC SinkID问题

问题描述 投票:0回答:1

我有8条虚拟音频线路。我试图将不同的有声视频播放器映射到不同的虚拟线路上,因为我们用它来进行远程解释。我的问题是当一个新的WebRTC出现时,所有的播放器都默认为最新的sinkID。水槽ID的报告他们没有改变,但所有的音频开始流经最近设置的sinkID。有人知道这是浏览器的限制还是什么吗?

这只发生在我在人们连接时按需创建音频播放器时。如果我在每个人都连接后设置ID,就没有问题。 我甚至尝试重新设置所有的sinkID,使其与每个新的连接相同,但音频仍然全部流经最新的音频播放器的SinkID。

我已经附上了所有东西的视图。在附件中,Dave将连接到SinkID的4号线。这工作得很好。如果另一个译员加入Tagalog,他们也可以听到4号线。问题是,如果有人加入让我们说西班牙语,当我设置播放器的水槽ID在西班牙语(3号线),所有的音频是通过3号线的SinkID听到,即使4号线的人的SinkID的仍然设置为4号线。 我想这可能是Chrome的一个BUG。我使用的是OpenTok(tokbox),一些不需要显示的PHP来管理什么语言显示。我已经附上了管理这一切的iFrame代码。

View of Interface

<!DOCTYPE html>
<html>
<head>
    <title>IRIS Remote Player</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <script src="../vendor/jquery/jquery.min.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <link href="/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://static.opentok.com/v2/js/opentok.js"></script>
    <link href="https://cdn.jsdelivr.net/gh/gitbrent/[email protected]/css/bootstrap4-toggle.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/gh/gitbrent/[email protected]/js/bootstrap4-toggle.min.js"></script>
    <meta name="lastLoaded" content="2020-04-21 11:34:57">
    <script> var EnglishOn; </script>
<style>
  .slow  .toggle-group { transition: left 0.7s; -webkit-transition: left 0.7s; }
  .fast  .toggle-group { transition: left 0.1s; -webkit-transition: left 0.1s; }
  .quick .toggle-group { transition: none;      -webkit-transition: none; }
</style>    
</head>
<body>
<button type="button" class="btn btn-lg btn-danger form-control" id="connect">Connect Remote Interpreters</button>
<div class="container-fluid">
    <div class="row live" style="display:none;background:black;color:white;padding-bottom:5px">
        <div class="col-sm-2"><input type="checkbox" checked data-width="100%" id="AllAudio" data-toggle="toggle" data-offstyle="danger" data-onstyle="success" data-on="Live" data-off="Break" data-style="quick"></div>
        <div class="col-sm-2"><input type="checkbox" checked data-width="100%" id="Admins" data-toggle="toggle" data-offstyle="danger" data-onstyle="success" data-on="Admins Live" data-off="Admins Off" data-style="quick"></div>
    </div>

    <div class="row live" style="display:none;background:#fff;color:black">
<div class="col-sm-2" id="PL"><input data-style="quick" type="checkbox" class="Interpretation AudioControl SwitchPL" data-width="100%" id="floorPolish" data-lang="PL" data-toggle="toggle" checked  data-offstyle="danger" data-onstyle="success" data-on="Polish" data-off="Floor"><br />Floor<br /><audio autoplay controls muted id="englishPolish" class="englishPL"></audio></div><div class="col-sm-2" id="RU"><input data-style="quick" type="checkbox" class="Interpretation AudioControl SwitchRU" data-width="100%" id="floorRussian" data-lang="RU" data-toggle="toggle" checked  data-offstyle="danger" data-onstyle="success" data-on="Russian" data-off="Floor"><br />Floor<br /><audio autoplay controls muted id="englishRussian" class="englishRU"></audio></div><div class="col-sm-2" id="ES"><input data-style="quick" type="checkbox" class="Interpretation AudioControl SwitchES" data-width="100%" id="floorSpanish" data-lang="ES" data-toggle="toggle" checked  data-offstyle="danger" data-onstyle="success" data-on="Spanish" data-off="Floor"><br />Floor<br /><audio autoplay controls muted id="englishSpanish" class="englishES"></audio></div><div class="col-sm-2" id="TL"><input data-style="quick" type="checkbox" class="Interpretation AudioControl SwitchTL" data-width="100%" id="floorTagalog" data-lang="TL" data-toggle="toggle" checked  data-offstyle="danger" data-onstyle="success" data-on="Tagalog" data-off="Floor"><br />Floor<br /><audio autoplay controls muted id="englishTagalog" class="englishTL"></audio></div><div class="col-sm-2" id="EN"><input type="checkbox" data-style="quick" class="AudioControl" data-width="100%" id="floorEnglish" data-toggle="toggle" data-offstyle="danger" data-onstyle="success" data-on="English" data-off="Floor"><br />Floor<br /><audio autoplay controls muted id="englishEnglish" class="englishEN"></audio></div>
    </div>
</div>




<script>
var audioSource;
var vars = {};
var floorSource;

$('#connect').click(function() {

    $('input:checkbox#Admins').change(function(){
        if ($(this).is(':checked')) {
            $('.ADMIN').each(function() {
                var adminID = $(this).attr("id");
                var thisID = adminID.replace("userID", "");
                $("#"+thisID).prop('muted', false);
                $('#'+thisID).prop('volume', 1);                    
            });         
        } else {
            $('.ADMIN').each(function() {
                var adminID = $(this).attr("id");
                var thisID = adminID.replace("userID", "");
                $("#"+thisID).prop('muted', true);
                $('#'+thisID).prop('volume', 0);                    
            });             
        }   
    });
    $('input:checkbox.AudioControl').change(function(){
        var LanguageCode = $(this).attr("data-lang");
        var LanguageName = $(this).attr("data-on");
        if ($(this).is(':checked')) {
            $("#english"+LanguageName).prop('muted', true);
            $('#english'+LanguageName).prop('volume', 0);   
            $("."+LanguageCode).prop('muted', false);
            $('.'+LanguageCode).prop('volume', 1);
            setTimeout(function(){ 
                if ($('#Admins').is(':checked')) {} else {
                    $('.ADMIN').each(function() {
                        var adminID = $(this).attr("id");
                        var thisID = adminID.replace("userID", "");             
                        $("#"+thisID).prop('muted', true);
                        $('#'+thisID).prop('volume', 0);    
                    }); 
                }   
            }, 200);
        } else {
            //Activate Floor
            $("#english"+LanguageName).prop('muted', false);
            $('#english'+LanguageName).prop('volume', 1);   
            $("."+LanguageCode).prop('muted', true);
            $('.'+LanguageCode).prop('volume', 0);
            setTimeout(function(){ 
                if ($('#Admins').is(':checked')) {} else {
                    $('.ADMIN').each(function() {
                        var adminID = $(this).attr("id");
                        var thisID = adminID.replace("userID", "");             
                        $("#"+thisID).prop('muted', true);
                        $('#'+thisID).prop('volume', 0);
                    });                 
                }       
            }, 200);                
        }
    });

    $('input:checkbox#AllAudio').change(function(){
        if ($(this).is(':checked')) {
            $('.Interpretation').bootstrapToggle('on')
            $('#floorEnglish').bootstrapToggle('off')
        } else {
            $('.AudioControl').bootstrapToggle('off')       
        }
    }); 

    $("#connect").hide();
    $("#subscriber").show();
    $(".live").show();
playFloor('Polish','PL');
        playFloor('Russian','RU');
        playFloor('Spanish','ES');
        playFloor('Tagalog','TL');
            playFloor('English', 'EN');

    function playFloor(language, code) {
        const audio = document.getElementById('english'+language);
        const constraints = {
            audio: {deviceId: floorSource},
            video: false
        };
        function handleSuccess(stream) {
          const audioTracks = stream.getAudioTracks();
          console.log('Got stream with constraints:', constraints);
          console.log('Using audio device: ' + audioTracks[0].label);
          stream.oninactive = function() {
            console.log('Stream ended');
          };
          window.stream = stream; // make variable available to browser console
          audio.srcObject = stream;
        }

        function handleError(error) {
          alert('navigator.MediaDevices.getUserMedia error: ', error.message, error.name);
        }

        newSinkID = vars[code];

        navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError);


        //const MyEnglishAudio = document.getElementById("englishPlayer");
        audio.setSinkId(newSinkID);
        console.log("Setting Audio to: "+newSinkID);

        if(code == "EN") {  
            $("#englishEnglish").prop('muted', false);
            $('#englishEnglish').prop('volume', 1); 
            console.log("Setting English Volume on "+floorSource);
        }       
    }
        function handleRemoteError(error) {
          if (error) {
            alert(error);
          }
        }




    var apiKey = 'HIDEEN';
    var sessionId = 'HIDDEN';
    var token = 'HIDDEN';

    socket = io.connect('/', {
        secure: true,
        'reconnection': true,
        'reconnectionDelay': 1000,
        'reconnectionAttempts': Infinity
    });


    socket.on('disconnect', function () {
            console.log("Disconnected from the server"); 
            //location.reload(true);            
        }); 

     socket.on('send', function (msg) {
         fromSocket = msg.split(" ");
         to_meeting_id = fromSocket[0];
         to_lang = fromSocket[1];
         action = fromSocket[2];
         who = fromSocket[3];

    if(to_meeting_id == '17') {      
        console.log("Action by "+who+": "+action+" for "+to_lang);
        var info = msg.split(" ");
        console.log("0: "+info[0]+" 1: "+info[1]+" 2: "+info[2]+" 3: "+info[3]+" 4:"+info[4]+" 5: "+info[5]+" 6: "+info[6]+" 7: "+info[7]);

        if(action == "english") {
            $('#floorEnglish').bootstrapToggle('on');
            $('.Switch'+info[7]).bootstrapToggle('off');
            useraudio = document.getElementById(who);
            MynewSinkID = vars['EN'];
            //useraudio.setSinkId(MynewSinkID);
            useraudio.setSinkId(MynewSinkID)
            .then(() => {
              console.log('successfully set the audio output device - Note Unmuting '+who);
                    $("#"+who).prop('muted', false);
                    $('#'+who).prop('volume', 1);
                    $('#'+who).css("background-color", "green");                    
            })
            .catch((err) => {
              console.error('Failed to set the audio output device ', err);
            });
        }
        if(action == "englishOff") {
            $('#floorEnglish').bootstrapToggle('off');
            $('.Switch'+info[6]).bootstrapToggle('on');
            // Set Sink Audio Back
            useraudio = document.getElementById(who);
            MynewSinkID = vars[info[6]];
            //useraudio.setSinkId(MynewSinkID);
            useraudio.setSinkId(MynewSinkID)
            .then(() => {
              console.log('successfully set the audio output device');
                    $('#'+who).css("background-color", "white");      
            })
            .catch((err) => {
              console.error('Failed to set the audio output device ', err);
            });         
        }   
            if(action == "privacyOn") {
                $('#floor'+info[4]).bootstrapToggle('off');
            }
            if(action == "privacyOff") {
                $('#floor'+info[4]).bootstrapToggle('on');
            }
    }

    }); 

    console.log("Starting Session");
      var session = OT.initSession(apiKey, sessionId);
        var AllUsers = [];
        i = 0;
      // Subscribe to a newly created stream
      session.on('streamCreated', function streamCreated(event) {
        console.log("New stream in the session: " + event.stream.streamId);
        var subscriberOptions = {
          insertMode: 'append',
          insertDefaultUI: false,
          subscribeToAudio: true,
          showControls: true,
          width: 400,
          height: 100,
          subscribeToVideo: false
        };

        console.log("STARTING LOG OF EVENT");
        console.log(event);
        console.log("END LOG OF EVENT");
        parms = event.stream.connection.data;
        //alert(parms)
        newparms = parms.split(",");
        ConnectingID = newparms[0];
        Channel = newparms[1];
        subscriber = session.subscribe(event.stream, subscriberOptions, handleRemoteError);
        console.log("Connecting to event stream: "+event.stream);
        //console.log("My Lang: "+MyLang+" VS "+Channel);
        item = {}
        item ["Channel"] = Channel;
        item ["RemoteUserID"] = ConnectingID;
        item ["RemoteStream"] = event.stream;
        // See if we have seen this user before. If so, remove them from the array so we can readd them after with new stream info
        jQuery.each(AllUsers, function(i, val) {
           if(val.RemoteUserID == ConnectingID) // delete index
           {
              //AllUsers.splice(i, 1); // Remove previous streams by this user
          }
        });
        AllUsers.push(item);
        console.log(AllUsers);

    subscriber.on('videoElementCreated', (event) => {
        //console.log("Info "+event.target.stream.connection.data);
        var connectionData = event.target.stream.connection.data;
        var info = connectionData.split(",")
        console.log("User ID: "+info[0]);
        console.log("Lang: "+info[1]);
        $('#userID'+info[0]).remove();
        $.ajax({
            type: 'POST',
            timeout: 10000,
            url: '/ajax/who.php',
            dataType: 'html',
            data: { "user_id": info[0] },
            cache: false,               
            error: function (jqXHR, exception) {
                var msg = '';
                if (jqXHR.status === 0) {
                    msg = 'Can not reach network.\n Verify you have internet.';
                } else if (jqXHR.status == 403) {
                    msg = 'Your five digit code did not match. ';
                } else if (jqXHR.status == 404) {
                    msg = 'Requested page not found. [404]';
                } else if (jqXHR.status == 500) {
                    msg = 'Internal Server Error [500].';
                } else if (exception === 'parsererror') {
                    msg = 'Requested JSON parse failed.';
                } else if (exception === 'timeout') {
                    msg = 'Time out error.';
                } else if (exception === 'abort') {
                    msg = 'Ajax request aborted.';
                } else {
                    msg = 'Uncaught Error.\n' + jqXHR.responseText;
                }
                alert("Local Server Request: "+msg);                        
            },
            success: function (data) {
                $('#'+info[1]).append(data);
                document.getElementById(info[1]).appendChild(event.element);
                setTimeout(function(){          
                    if (data.indexOf('ADMIN') > -1) {
                        if ($('#Admins').is(':checked')) {} else {
                            $("#"+info[0]).prop('muted', true);
                            $('#'+info[0]).prop('volume', 0);
                        }
                    }
                }, 200);
            }
        });         

        console.log("Video Element Created");
        console.log(event);
        dynamicSinkID = vars[info[1]];
        //alert(dynamicSinkID);
        event.element.setAttribute("class", "InterpreationSource "+info[1]);
        event.element.setAttribute("data-lang", info[1]);
        event.element.setAttribute("id", info[0]);
        event.element.setAttribute("controls", "controls");
        event.element.setAttribute("width", "300");
        event.element.setAttribute("height", "54");
      if (typeof event.element.sinkId !== 'undefined') {
        event.element.setSinkId(dynamicSinkID)
        .then(() => {
          console.log('successfully set the audio output device');
        })
        .catch((err) => {
          console.error('Failed to set the audio output device ', err);
        });
      } else {
        console.warn('device does not support setting the audio output');
      }



      // FIX FOR CHROME BUG SETTING LAST PLAYER CREATED AS THE SINKID FOR All - FAILED
      /*
      setTimeout(function(){
            $('.InterpreationSource').each(function() {

                var playerID = $(this).attr("id");
                var lang_code = $(this).attr("data-lang"); // Get language of each player
                var FixID = vars[lang_code]; // Get SinkID it's supposed to be
                var theFix = document.getElementById(playerID);
                console.log("Fixing "+playerID+" with "+FixID);
                theFix.setSinkId(FixID)
                .then(() => {
                  console.log('successfully set the audio output device - Note Unmuting '+playerID);    
                })
                .catch((err) => {
                  console.error('Failed to set the audio output device ', err);
                });                 
            });     

        }, 2000);
        */      
      // END FIX BUG FOR CHROME

    });         

      });

    session.connect(token, function (error) {
        if(error) {
            alert(error);
        }
    }); 



});


if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
  console.log("enumerateDevices() not supported.");
}

// List cameras and microphones.

navigator.mediaDevices.enumerateDevices()
.then(function(devices) {
  devices.forEach(function(device) {
    //console.log(device.kind + ": " + device.label +" id = " + device.deviceId);
    if(device.kind == "audiooutput" && device.label == "Line 5 (Virtual Audio Cable)") {
        vars['EN'] = device.deviceId;
        console.log(device.kind + ": " + device.label +" id = " + device.deviceId);
    }
    if(device.kind == "audiooutput" && device.label == "Line 1 (Virtual Audio Cable)") {
        vars['PL'] = device.deviceId;
        console.log(device.kind + ": " + device.label +" id = " + device.deviceId);
    }
    if(device.kind == "audiooutput" && device.label == "Line 2 (Virtual Audio Cable)") {
        vars['RU'] = device.deviceId;
        console.log(device.kind + ": " + device.label +" id = " + device.deviceId);
    }
    if(device.kind == "audiooutput" && device.label == "Line 3 (Virtual Audio Cable)") {
        vars['ES'] = device.deviceId;
        console.log(device.kind + ": " + device.label +" id = " + device.deviceId);
    }
    if(device.kind == "audiooutput" && device.label == "Line 4 (Virtual Audio Cable)") {
        vars['TL'] = device.deviceId;
        console.log(device.kind + ": " + device.label +" id = " + device.deviceId);
    }
    if(device.kind == "audioinput" && device.label == "Microphone Array (Synaptics Audio)") {
        floorSource = device.deviceId;
        console.log(device.kind + ": " + device.label +" id = " + device.deviceId);
    }   
  });
})
.catch(function(err) {
  console.log(err.name + ": " + err.message);
});


</script>
</body>
</html>

https:/groups.google.comforum?utm_medium=email&utm_source=footer#!msgdiscuss-webrtcvrw44ZGE0gs2YJ6yUEjBgAJ。

WebRTC 在内部混合音轨,所有音轨的音频实际上是以单一音频流的形式传送到 Chrome。对音量等属性的更改,是在混音前送入WebRTC应用的。 所以,很不幸的是,这意味着该混音器的输出只能指向单个音频设备。

上述情况适用于和媒体元素。

然而,在Chrome浏览器中有一个可用的变通方法,它涉及到通过WebAudio进行渲染。 要做到这一点,音频仍然需要从混合流中 "拉 "出来,即使它不一定要去一个特定的设备或静音.然后你可以按照这个例子来克隆远程轨道,并通过WebAudio渲染它们。

所以现在我只需要弄清楚如何做到这一点。

html audio webrtc
1个回答
0
投票

所以我想出了如何解决这个问题。我创建了一个函数,并使用以下代码通过WebAudio循环播放WebRTC音频。

    function addNewStream(event, dynamicSinkID, lang) {     
        var mediaStream = event.element.srcObject;
        var remote_stream1 = mediaStream.clone();
        test_audio_context1 = new AudioContext();
        webaudio_source1 = test_audio_context1.createMediaStreamSource(remote_stream1);
        webaudio_ms1 = test_audio_context1.createMediaStreamDestination();
        webaudio_source1.connect(webaudio_ms1);
        test_output_audio1 = new Audio();
        test_output_audio1.srcObject  = webaudio_ms1.stream;
        test_output_audio1.play();
        test_output_audio1.setSinkId(dynamicSinkID);
        test_output_audio1.setAttribute("controls", "controls");
        document.getElementById(lang).appendChild(test_output_audio1);
        test_output_audio1.setAttribute("controls", "controls");
        test_output_audio1.setAttribute("width", "300");
        test_output_audio1.setAttribute("height", "54");                          
    }
© www.soinside.com 2019 - 2024. All rights reserved.