Странная проблема WebRT C SinkID - PullRequest
0 голосов
/ 17 апреля 2020

У меня 8 виртуальных аудио линий. Я пытаюсь отобразить разные аудио / видео плееры на разные виртуальные линии, поскольку мы используем это для удаленной интерпретации. Моя проблема в том, что когда появляется новый WebRT C, все проигрыватели по умолчанию используют новейший вид sinkID. Отчет идентификатора приемника не изменился, но все аудио начинает проходить через последний установленный идентификатор приемника. Кто-нибудь знает, это ограничение браузера или что-то?

Это происходит только тогда, когда я создаю аудиоплееры по требованию, когда люди подключаются. Нет проблем, если я установлю идентификаторы после того, как все подключены. Я даже пытаюсь сбросить все идентификаторы стоков, чтобы они были такими, какими они должны быть при каждом новом подключении, но звук все равно проходит через SinkID на новейшем аудиоплеере.

Я прикрепил представление о том, как все выглядит. В прикрепленном виде Дэйв будет привязан к SinkID для строки 4. Это прекрасно работает. Если другой переводчик присоединяется к Tagalog, его также можно услышать в строке 4. Проблема в том, что если кто-то присоединяется, скажем, Spani sh, когда я устанавливаю идентификатор приемника для проигрывателя на Spani sh (строка 3), все аудио слышно через SinkID в строке 3, хотя SinkID для людей в строке 4 по-прежнему настроено на строку 4. Я думаю, что это может быть Chrome ошибка. Я использую 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/bootstrap4-toggle@3.6.1/css/bootstrap4-toggle.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/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.com/forum/?utm_medium=email&utm_source=footer# ! msg / обсуждения-webrtc / vrw44ZGE0gs / 2YJ6yUEjBgAJ

WebRT C внутренне микширует звуковые дорожки, и аудио для всех дорожек фактически доставляется на Chrome в виде единого звукового потока. Изменения в свойствах, таких как громкость, отправляются в WebRT C для применения перед смешиванием. Таким образом, это, к сожалению, означает, что выход из этого микшера может быть направлен только на одно аудиоустройство.

Вышесказанное относится и к элементам мультимедиа.

Однако в * есть обходной путь 1033 *, что включает рендеринг через WebAudio. Чтобы это работало, аудио все равно нужно «извлекать» из смешанного потока, даже если оно не обязательно go на конкретное устройство или отключено. Затем вы можете следовать этому примеру, чтобы клонировать удаленные треки и визуализировать их через WebAudio.

Так что теперь мне просто нужно выяснить, как это сделать.

1 Ответ

0 голосов
/ 24 апреля 2020

Итак, я понял, как решить эту проблему. Я создал функцию и закачал аудио через WebRT C через WebAudio, используя следующий код:

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