WebRTC屏幕在移动设备

时间:2017-10-17 16:18:33

标签: angular mobile webrtc

我正在使用WebRTC在Angular 2应用程序上创建通信。此应用程序用于具有CSS规则的笔记本电脑和智能手机,以处理响应。

我有一个特定问题:我的应用程序在笔记本电脑上的两个网页之间完美地工作,但是当我在智能手机上打开网页时,页面会在通信开始时冻结(智能手机和笔记本电脑之间)。该页面仅在智能手机端冻结,所有这些都在笔记本电脑上正常工作。

我仅在Chrome上有这种行为。一切都适用于Firefox。

问题是解决了#34;当我评论addStream()函数时。现在,我使用addTrack()函数代替addStream(),但问题仍然存在。

这是我创建和管理WebRTC通信的服务。我使用visioApi通过信令服务器发送数据。

interface IceCandidateObject {
    candidate: string
    sdpMid: string
    sdpMLineIndex: number
  }

@Injectable()
export class VisioService {
    conf: RTCConfiguration = {
        iceServers: [
          // Peer Configuration
          {
            urls: 'xxxxxx',
            credential: 'xxxxxx',
            username: 'xxxxxx',
          },
          {
            urls: 'xxxxx',
            credential: 'xxxxxx',
            username: 'xxxxxx',
          },
          {
            urls: 'xxxxxxxx',
            credential: 'xxxxxx',
            username: 'xxxxx',
          },
        ],
      }
      pc: any                                                                               // Peer connection
      stream: any                                                                           // Video stream
      target: string                                                                        // target userkey
      remoteVideo = undefined
      constraints = {
        mandatory: [{ OfferToReceiveAudio: true }, { OfferToReceiveVideo: true }],
        optional: [{ DtlsSrtpKeyAgreement: true }],
      }
      callId: string
      caller = false
      videoRunning = true
      audioRunning = true

    constructor(
        private visioApi: ApiVisio,
        private zpConnection: ZetaPushConnection,
    ) {
        this.visioApi.onReplyToCall.subscribe((onReplyToCallMsg) => {
            console.log('VisioService::OnReplyToCall')
            this.callId = onReplyToCallMsg.result.id
        })

        this.visioApi.onCallUser.subscribe((onCallUserMsg) => {
            console.log('VisioService::OnCallUser')
            this.callId = onCallUserMsg.result.callObject.id
        })

        this.visioApi.onSendVisioMessage.subscribe((onSendVisioMsg) => {
            const message = onSendVisioMsg.result.message

            switch (message.type) {

                case 'offer':
                    this.pc.setRemoteDescription(new RTCSessionDescription(message), () => {
                        this.pc.createAnswer().then((answer) => {
                            this.visioApi.sendVisioMessage(this.target, answer)
                            return this.pc.setLocalDescription(answer)
                        }).catch((err) => {
                            console.log('FailedCreateAnswer', err)
                        })
                    }, (err) => {
                        console.log('SetRemoteDescriptionFailed', err)
                    })
                    break

                case 'answer':
                    const answerSessionDescription: RTCSessionDescriptionInit = {
                        sdp: message.sdp,
                        type: message.type
                    }

                    this.pc.setRemoteDescription(new RTCSessionDescription(answerSessionDescription))
                    break

                case 'icecandidate':
                    const ice: RTCIceCandidateInit = {
                        candidate: message.candidate,
                        sdpMid: message.sdpMid,
                        sdpMLineIndex: message.sdpMLineIndex
                    }

                    this.pc.addIceCandidate(new RTCIceCandidate(ice))
                    break
            }
        })
    }

    /**
     * Method used to launch a visio call with the other user
     * @param target : userKey of the target
     */
    launchVisioCall(target: string): void {
        this.target = target
        this.visioApi.callUser(this.target)
    }

    /**
     * Stop the call
     */
    stopCall(): void {
        if (this.pc && this.pc.signalingState !== 'closed') {
            this.pc.close()
        }
    }

    /**
     * Method to reply to an incoming call
     */
    ReplyToIncomingCall(): void {
        this.visioApi.replyToCall(this.callId, this.target)
    }

    /**
     * Method to refuse an incoming call
     */
    refuseIncomingCall(): void {
        this.visioApi.refuseCall(this.callId, this.target)
    }

    /**
     * Method to set the call id
     * @param id : Id of the call
     */
    setCallId(id: string): void {
        this.callId = id
    }

    /**
     * Method to set the target
     * @param target : userKey of the target
     */
    setTarget(target: string): void {
        this.target = target
    }

    /**
     * Init
     * @param videoElement : remote video
     * @param videoRemote : local video
     */
    init(videoElement: HTMLVideoElement, videoRemote: HTMLVideoElement, caller: boolean): void {
        this.caller = caller
        this.startLocalVideo(videoElement)
        this.remoteVideo = videoRemote
    }

    /**
     *  Start the local video
     */
    startLocalVideo(videoElement: HTMLVideoElement): void {
        videoElement.volume = 0;
        videoElement.muted = false;
        videoElement.load();
        videoElement.play().then((result) => {
            console.log('==> video played', result)
        }).catch((err) => {
            console.error('==> video error', err)
        });

        navigator.mediaDevices.getUserMedia( { 
            audio: true,
            video: true
        }).then((stream) => {
            videoElement.srcObject = stream
            this.stream = stream
            this.initPeerConnection()
        }).catch((err) => {
            console.error('err local video', err)
        })
    }

     /**
     * Init peer connection
     */
    initPeerConnection(): void {
        this.pc = new RTCPeerConnection(this.conf)

        this.stream.getTracks().forEach(element => {
            this.stream.addTrack(element);
            if (this.pc.addTrack) {
                this.pc.addTrack(element, this.stream);
            } else {
                setTimeout(() => this.pc.dispatchEvent(new Event('negociationneeded')))
            }
        });

        this.pc.onnegociationneeded = event => {
            this.pc.createOffer().then((offer) => {
                this.pc.setLocalDescription(offer)
            }).then(() => {
                const offerMsg: Message = {
                    sdp: this.pc.localDescription,
                    type: 'offer'
                }
                this.visioApi.sendVisioMessage(this.target, offerMsg)
            })
        }


        this.pc.addStream(this.stream)
        // Handle ICE Candidates
        this.pc.onicecandidate = event => {
          const iceMessage: Message = {
            candidate: event.candidate.candidate,
            sdpMid: event.candidate.sdpMid,
            sdpMLineIndex: event.candidate.sdpMLineIndex,
            type: event.type,
          }

          if (event.candidate != null) {
            this.visioApi.sendVisioMessage(this.target, iceMessage)
          }
        }

        // Handle new stream added
        this.pc.onaddstream = event => {
          console.log('onAddStream::Event', event)

          const video = this.remoteVideo
          video.srcObject = event.stream
          video.load();
          video.play()
        }

        if (!this.caller) {
            this.sendOffer()
        }
    }

    sendOffer(): void {
         // We create an offer when the target accept the call
        this.pc.createOffer(offer => {
            this.pc.setLocalDescription(new RTCSessionDescription(offer)).then(() => {
                const offerMsg: Message = {
                    sdp: offer.sdp,
                    type: offer.type
                }
                this.visioApi.sendVisioMessage(this.target, offerMsg)
            })
        }, (err) => {
            console.log('ErrorCreateOffer', err)
        }, this.constraints)
    }

    toggleAudio(): void {
        this.stream.getTracks().forEach(element => {
            if (element.kind === 'audio') {
                element.enabled = !element.enabled
            }
        })
    }

    toggleVideo(): void {
        this.stream.getTracks().forEach(element => {
            if (element.kind === 'video') {
                element.enabled = !element.enabled
            }
        })
    }

    terminateCall(): void {
        this.visioApi.terminateCall(this.callId, this.target)
    }

    stopWebcam(): void {
        this.stream.getTracks().forEach(track => {
            track.stop()
        })

我在每个平台上都使用Chrome 61,而我的智能手机则在Android上使用。

我希望有人帮助我。

谢谢,

达明

1 个答案:

答案 0 :(得分:0)

有一个chrome 61 bug。

要解决此问题,请参阅Philipp Hancke(Opentok on streamCreated subscribe makes mobile chrome freeze

的答案

错误的详细信息:https://bugs.chromium.org/p/chromium/issues/detail?id=769148

解决方法:在每个html视频上放置border-radius: 1px;