【问题标题】:How to implement a voice(and later video)chat between Vr devices and an angular frontend?如何在 Vr 设备和 Angular 前端之间实现语音(以及后来的视频)聊天?
【发布时间】:2019-11-19 15:54:50
【问题描述】:

嘿,我正在开发一个应用程序,它通过将数据发送到 Asp.net Core 3.1 Api 将 Vr 设备(统一)与角度前端连接起来。

嗯,整个连接是通过使用 SignalR Core 发送 json 对象来工作的。但是新的要求之一是两个客户端都可以通过使用语音聊天(以及后来的视频)相互通信。在我的研究中,我发现 SignalR 不是为了进行此类调用而创建的。

我们还发现CometChat 仅适用于前端,不适用于 asp.net core 或 Vr 设备。 Agora 将语音数据发送到自己的服务器,然后再返回,但因为它是敏感数据,所以必须保留在我们自己的服务器上(如 asp.net core 3.0)。

有什么想法吗?

[更新]

嗨, 我们有 3 个应用程序。

1 Unity(平台 WebGL)

2 Asp.net Core 3.1 WebAPI。

3 Angular(7.x.x) 前端

1-2 和 2-3 之间的通信是通过 SignalR 进行的。

在一个教程中,我们在 Angular 项目中实现了 simple-peer,并在 3-2-3 之间建立了连接。我们现在希望在 1-2-3 或 3-2-1 之间建立连接。

问题是我们不知道如何在 Unity 中实现 WebRTC 或 simple-peer 来建立这种连接(点对点)。

我们已经购买并尝试了this assetthis unity 包,但我不知道如何实现它。

请帮忙?我们被困住了

【问题讨论】:

  • 角前端是在浏览器中运行还是在电子中运行?
  • 只在浏览器中

标签: angular unity3d asp.net-core signalr virtual-reality


【解决方案1】:

最好的办法是使用标准语音聊天插件,例如 Unity 客户端的 WebRTC Video Chat

然后,仅使用插件创建一个场景并将 WebGL 播放器直接嵌入到您的 HTML 页面中(您称之为 Angular 前端)。这样,您可以确保非常严格的兼容性并保持在插件作者实际测试的范围内,尽管任何基于 WebRTC 的视频会议都可能兼容。

您还可以尝试移植(或研究)Dissonance 插件的代码以提高语音聊天的质量。

【讨论】:

  • 对不起,我没有回复。我已经更新了我的问题。
【解决方案2】:

我不确定它对你有多大帮助,有一个简单的 stackblitz 与 webRTC 的集成。

在 app.module.ts 中进行如下修改:

const config = {
 apiKey: "AIzaSyCpqI773ach6MxOcIqRvgDFBVVTvLJW-Ew",
 authDomain: "sample6-d38d3.firebaseapp.com",
 databaseURL: "https://sample6-d38d3.firebaseio.com",
 projectId: "sample6-d38d3",
 storageBucket: "gs://sample6-d38d3.appspot.com",
 messagingSenderId: "358754973898"
};

@NgModule({
   imports: [
            BrowserModule,
            AngularFireModule.initializeApp(config),
            AngularFireDatabaseModule
           ],
   declarations: [AppComponent, MeetingComponent],
   bootstrap: [AppComponent]
})
 export class AppModule { }

添加新组件如下:

import {
  Component,
  OnInit,
  ViewChild,
  ElementRef,
  NgZone
} from '@angular/core';
import {
  AngularFireDatabase,
  AngularFireList
} from 'angularfire2/database';
import * as firebase from "firebase/app";
import {
  Observable
} from 'rxjs';
declare
let RTCPeerConnection: any;

@Component({
  selector: 'app-meeting',
  templateUrl: './meeting.component.html',
  styleUrls: ['./meeting.component.css']
})
export class MeetingComponent implements OnInit {
  callActive: boolean = false;
  pc: any;
  localStream: any;
  channel: AngularFireList < {} > ;
  database: firebase.database.Reference;
  senderId: string;

  @ViewChild("me") me: any;
  @ViewChild("remote") remote: any;

  constructor(
    private afDb: AngularFireDatabase,
  ) {}

  ngOnInit() {
    this.setupWebRtc();
  }

  public ngOnDestroy() {
    this.pc.close();
    let tracks = this.localStream.getTracks();
    for (let i = 0; i < tracks.length; i++) {
      tracks[i].stop();
    }
    this.callActive = false;
  }

  setupWebRtc() {
    this.senderId = this.guid();
    var channelName = "/webrtc";
    this.channel = this.afDb.list(channelName);
    this.database = this.afDb.database.ref(channelName);
    this.database.on("child_added", this.readMessage.bind(this));

    try {
      this.pc = new RTCPeerConnection({
        iceServers: [{
            urls: "stun:stun.services.mozilla.com"
          },
          {
            urls: "stun:stun.l.google.com:19302"
          }
        ]
      }, {
        optional: []
      });
    } catch (error) {
      console.log(error);
      this.pc = new RTCPeerConnection({
        iceServers: [{
            urls: "stun:stun.services.mozilla.com"
          },
          {
            urls: "stun:stun.l.google.com:19302"
          }
        ]
      }, {
        optional: []
      });
    }


    this.pc.onicecandidate = event => {
      event.candidate ? this.sendMessage(this.senderId, JSON.stringify({
        ice: event.candidate
      })) : console.log("Sent All Ice");
    }

    this.pc.onremovestream = event => {
      console.log('Stream Ended');
    }

    this.pc.ontrack = event =>
      (this.remote.nativeElement.srcObject = event.streams[0]); // use ontrack
    this.showMe();
  }

  sendMessage(senderId, data) {
    var msg = this.channel.push({
      sender: senderId,
      message: data
    });
    msg.remove();
  }

  readMessage(data) {
    if (!data) return;
    try {
      var msg = JSON.parse(data.val().message);
      let personalData = data.val().personalData;
      var sender = data.val().sender;
      if (sender != this.senderId) {
        if (msg.ice != undefined && this.pc != null) {
          this.pc.addIceCandidate(new RTCIceCandidate(msg.ice));
        } else if (msg.sdp.type == "offer") {
          this.callActive = true;
          this.pc.setRemoteDescription(new RTCSessionDescription(msg.sdp))
            .then(() => this.pc.createAnswer())
            .then(answer => this.pc.setLocalDescription(answer))
            .then(() => this.sendMessage(this.senderId, JSON.stringify({
              sdp: this.pc.localDescription
            })));
        } else if (msg.sdp.type == "answer") {
          this.callActive = true;
          this.pc.setRemoteDescription(new RTCSessionDescription(msg.sdp));
        }
      }
    } catch (error) {
      console.log(error);
    }
  }

  showMe() {
    navigator.mediaDevices.getUserMedia({
        audio: true,
        video: true
      })
      .then(stream => (this.me.nativeElement.srcObject = stream))
      .then(stream => {
        this.pc.addStream(stream);
        this.localStream = stream;
      });
  }

  showRemote() {
    try {
      this.pc.createOffer()
        .then(offer => this.pc.setLocalDescription(offer))
        .then(() => {
          this.sendMessage(this.senderId, JSON.stringify({
            sdp: this.pc.localDescription
          }));
          this.callActive = true;
        });
    } catch (error) {
      this.setupWebRtc();
      console.log(error);
    }
  }

  hangup() {
    this.pc.close();
    let tracks = this.localStream.getTracks();
    for (let i = 0; i < tracks.length; i++) {
      tracks[i].stop();
    }
    this.callActive = false;
  }

  guid() {
    return (this.s4() + this.s4() + "-" + this.s4() + "-" + this.s4() + "-" + this.s4() + "-" + this.s4() + this.s4() + this.s4());
  }
  s4() {
    return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
  }

}
<div>
  <video id="device" controls autoplay #remote>
	</video>
  <video id="me" controls autoplay #me></video>
</div>
<button mat-raised-button color="primary" (click)="showRemote()" [disabled]="callActive">Connect</button>
<button mat-raised-button color="warn" (click)="hangup()" [disabled]="!callActive">Disconnect</button>

我已在此处添加此代码,以防此链接损坏。

Stackblitz

【讨论】:

  • 很遗憾它没有。这仅显示 Angular 的设置,但不包括 Unity 设置或后端。
  • 这里是 node js 和简单的 html 和 javascript 的链接,通过 angular 应用程序,您可以轻松使用 node js。 sitepoint.com/webrtc-video-chat-application-simplewebrtc
  • 静止。它在统一 C# 部分没有说明任何内容。仅角度部分就有很多可用的信息。但不是在统一部分。主要问题是两者的结合。
猜你喜欢
  • 2012-10-26
  • 2011-06-18
  • 2014-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-09
  • 1970-01-01
  • 2015-02-12
相关资源
最近更新 更多