1


我正在使用 Janus VideoRoom 插件,我想订阅/取消订阅 2 个或更多不同的发布者。
前 4 或 5 个订阅(开始订阅 -> 让视频可见)需要 2-3 秒。
我称之为正常
但下一次订阅需要更多时间。
我称之为不正常
请告诉我我的错误在哪里?

这是我的 JS(打字稿)

declare var Janus: any;
export class SubscriberClientSimple {
    private login:string;
    private opaqueId:string;
    private videoPlayer:any;
    private streamName:string;
    private serverUri:string;
    private serverURL:string;

    private api:any;
    private plugin:any;
    private remoteStream:any;

    public static STARTING:string = "STARTING";
    public static STARTED:string = "STARTED";
    public static STOPPING:string = "STOPPING";
    public static STOPPED:string = "STOPPED";

    private state:string;

    constructor(login:string){
        this.login = login;
        this.opaqueId = "subscriber-" + this.login + "-" + Janus.randomString(12);
    }

    public destroy():void{
    }
    
    public setPlayerElement(element:any):void{
        this.videoPlayer = element;
    }
    public hasVideoPlayerElement():boolean{
        return this.videoPlayer!=null;
    }
    
    public isAudioEnabled():boolean{
        return false;
    }
    public isVideoEnabled():boolean{
        return true;
    }

    public getStreamName():string{
        return this.streamName;
    }

    public subscribe(streamName: string, serverUri: string, audio:boolean, video:boolean):void{
        this.log("Start requested");
        this.state = SubscriberClientSimple.STARTING;
        this.streamName = streamName;
        this.serverUri = serverUri;
        this.serverURL = MEDIA_SERVER_URL + MEDIA_SERVER_PATH + "/janus-debug";

        EventBus.dispatchEvent(StreamingEvent.REMOTE_VIDEO_RECEIVE_INTENT, null);

        //this.log("server url : "+this.serverURL);

        Janus.init({
            userId: parseInt(this.login),
            backendUrl: this.serverURL,
            environment: environment,
            debug: false, // fixme это вынужденно отключено, чтобы можно было использовать Janus.noop как коллбэк для логирования
            callback: () => this.initCallback()
        });
    }

    public unsubscribe():void{
        this.log("unsubscribe state="+this.state);
        if(this.state == SubscriberClientSimple.STARTED){
            this.state = SubscriberClientSimple.STOPPING;
            
            EventBus.dispatchEvent(StreamingEvent.SUBSCRIBER_BUSY, null);
            
            if(this.plugin){
                this.log("sending leave command...");
                this.plugin.send({message: {request: "leave"}});
            }
        }
    }
    
    public disableAudio():void{
        this.log("Disable AUDIO");
    }
    public disableVideo():void{
        this.log("Disable VIDEO");
    }
    
    public enableAudio():void{
    }
    public enableVideo():void{
    }
    
    public reset():void{
    }
    
    public setSocketService(socketService:any):void{
    }
    public setModalService(modalService):void{
    }

    public isMyCurrentPublisher(id:string):boolean{
        return this.streamName == id;
    }
    
    public getSubscribeState():any{
        var data:any = {userId:this.login, audio: {state:""}, video:{state:"OFF"}};
        data.audio.state = "OFF";
        
        if(this.state == SubscriberClientSimple.STARTED){
            data.video.state = "ON";
        }
        return data;
    }
    
    public isSubscribing():boolean{
        return this.state == SubscriberClientSimple.STARTED;
    }

    private initCallback():void{
        this.log("initCallback");
        if (!Janus.isWebrtcSupported()) {
            this.onWebRTCNotSupported();
        }
        this.createSession();
    }

    private createSession():void {
        // Create session
        this.log("createSession");
        this.api = new Janus({
            server: this.serverUri,
            iceServers: ICE_SERVERS,
            success: () => this.onStreamingServerCreateSuccess(),
            error: (err) => this.onStreamingServerCreateError(err),
            destroyed: () => this.onStreamingServerDestroyed()
        });
    }

    private onStreamingServerCreateSuccess():void{
        this.log("onStreamingServerCreateSuccess");
        this.attachPlugin();
    }

    private attachPlugin():void{
        this.log("attachPlugin");
        let remoteFeed = null;
        this.api.attach(
            {
                plugin: "janus.plugin.videoroom",
                opaqueId: this.opaqueId,
                success: (pluginHandle) => {
                    this.log("plugin attached");
                    remoteFeed = pluginHandle;
                    remoteFeed.simulcastStarted = false;

                    // We wait for the plugin to send us an offer
                    remoteFeed.send({
                        "message": {
                            "request": "listparticipants",
                            "room": +this.streamName
                        },

                        success: (res) => {
                            this.log("Total participants="+res.participants.length);

                            var canJoin:boolean = res.participants && res.participants.length > 0;

                            this.log("canJoin="+canJoin);

                            if (canJoin) {
                                var publisherParticipant:any = res.participants[0];
                                var publisherId:string = publisherParticipant.id;

                                const subscriptionData:any = {
                                    "request": "join",
                                    "room": +this.streamName,
                                    "ptype": "subscriber",
                                    "feed": publisherId,
                                    "private_id": parseInt(this.login)
                                };

                                this.log("subscriptionData="+JSON.stringify(subscriptionData));

                                remoteFeed.send({"message": subscriptionData});
                            }
                            else {
                                this.log("Requested stream is not available anymore.");
                            }
                        }
                    });
                },
                error: (error) => {
                    const message = "Error attaching subscribe plugin. " + error.toString();
                    this.log(message);
                },
                onmessage: (msg, jsep) => {
                    this.onPluginMessage(msg, jsep, remoteFeed);
                },
                webrtcState: (on, reason) => {
                    if (reason === "Close PC") {
                        var message:string = "Closed PeerConnection";
                        this.log(message);
                    }
                },
                iceState: (state) => {
                    this.onICEState(state);
                },
                onlocalstream: function (stream) {
                    // The subscriber stream is recvonly, we don't expect anything here
                },
                onremotestream: (stream) => {
                    this.onSubscriberGotRemoteStream(stream);
                },
                oncleanup: () => {
                    this.onCleanUp();
                }
            });
    }

    private onPluginMessage(msg, jsep, remoteFeed):void{
        this.log("onPluginMessage");
        this.log("msg:"+JSON.stringify(msg));
        this.log("jsep:"+JSON.stringify(jsep));
        this.log("remoteFeed:"+remoteFeed);

        const event = msg["videoroom"];

        if (msg["error"]){
            if (msg["error"] !== "No such room") {
                this.log("Janus ERROR:"+msg["error"]);
            }
        }
        else if (event) {
            if (event === "attached") {
                // Subscriber created and attached
                this.log("Subscriber created and attached");
                if (this.plugin === undefined || this.plugin === null) {
                    this.plugin = remoteFeed;
                }

                remoteFeed.rfid = msg["id"];
                remoteFeed.rfdisplay = msg["display"];
            }
            else if(event === "event"){
                var leftRoomResult:any = msg["left"];

                if(leftRoomResult){
                    if(leftRoomResult == "ok"){
                        var roomId:string = msg["room"].toString();
                        this.onLeftRoom(roomId);
                    }
                }
            }
            else if (event === "slow_link") {
            }
        }
        if (jsep) {
            // Answer and attach
            this.log("create answer");
            
            remoteFeed.createAnswer(
                {
                    jsep: jsep,
                    media: {audioSend: false, videoSend: false},    // We want recvonly audio/video

                    success: (_jsep) => {
                        this.log("Got SDP!", _jsep);
                        const body = {"request": "start", "room": this.streamName};
                        remoteFeed.send({"message": body, "jsep": _jsep});
                    },
                    error: (err) => {
                        this.log("WebRTC error:", err);
                    }
                });
        }
    }

    private onSubscriberGotRemoteStream(stream:any):void{
        this.log("onSubscriberGotRemoteStream this.state="+this.state);
        if(this.state == SubscriberClientSimple.STARTING){
            const videoTracks = stream.getVideoTracks();

            this.remoteStream = stream;

            this.attachRemoteStream();
            this.startRemoteVideo();

            this.state = SubscriberClientSimple.STARTED;
        }
    }

    private onLeftRoom(roomId:string):void{
        if(roomId == this.streamName){

            this.log("Left room");

            this.plugin.ondetached();
            this.plugin.detach();

            this.plugin = null;
            this.log("VIDEO stopped");
            this.state = SubscriberClientSimple.STOPPED;
            EventBus.dispatchEvent(StreamingEvent.REMOTE_VIDEO_DISABLED, null);
        }
    }

    private onCleanUp():void{
        this.log("onCleanUp");
    }

    private onICEState(state:string):void{
        this.log("ICE state of this WebRTC PeerConnection changed to " + state);
    }

    private attachRemoteStream():void {
        Janus.attachMediaStream(this.videoPlayer, this.remoteStream );
        this.videoPlayer.muted = "muted";
    }
    private startRemoteVideo():void {
        this.videoPlayer.play();
        this.log("VIDEO started");
        EventBus.dispatchEvent(StreamingEvent.REMOTE_VIDEO_RECEIVING_STARTED, null);
    }

    private onStreamingServerCreateError(err:any):void{
        this.log("Streaming Server Error:",err);

    }
    private onStreamingServerDestroyed():void{
        this.log("Janus destroyed");
    }

    private onWebRTCNotSupported():void{
        this.log("WebRTC Not Supported");
    }

    protected log(value:any, ...rest:any[]):void{
        EventBus.dispatchEvent(AppEvent.SEND_LOG, {className:this.getClassName(), value:value, rest:rest});
    }
    protected getClassName():string{
        return "SubscriberClientSimple";
    }
}


这是我在 4 或 5 次正常订阅启动后的日志//观看
发布者的视频
// 停止上一个订阅
04:01:50 取消订阅状态= 已开始
04:01:50 发送离开命令...
04:01:50 取消订阅状态=STOPPING
04:01:50 unsubscribe state=STOPPING
04:01:50 onPluginMessage
04:01:50 msg:{"videoroom":"event","room":681365,"left":"ok"}
04:01:50 jsep:undefined
04:01:50 remoteFeed:[object Object]
04:01:50 离开房间
04:01:50 onCleanUp
04:01:50 视频停止
04:01:50 Closed PeerConnection
04:01:50 onCleanUp
// new订阅正常开始
04:01: 53 开始请求
04:01:53 initCallback
04:01:53 createSession
04:01:54 onStreamingServerCreateSuccess
04:01:54 attachPlugin 04:01:54
附加插件
04:01:54 参与者总数=1
04:01:54 canJoin=true
04: 01:54 subscriptionData={"request":"join","room":724240,"ptype":"subscriber","feed":3606687285964170,"private_id":680291}
04:01:54 onPluginMessage
04:01: 54 消息:{"videoroom":"attached","room":724240,"id":3606687285964170,"display":"724240"}
04:01:54 jsep:{"type":"offer","sdp":"v=0\r\no=- 1615780141548001 1 IN IP4 185.12.12.24\r\ns=VideoRoom 724240\r\nt=0 0\r\na=group:BUNDLE audio video\r\na=msid-semantic: WMS janus\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111\r\nc=IN IP4 185.12.12.24\r\ na=sendonly\r\na=mid:audio\r\na=rtcp-mux\r\na=ice-ufrag:NVx5\r\na=ice-pwd:Tz3hjJNIxACUULUbBBREaL\r\na=ice-options:trickle\ r\na=指纹:sha-256 53:C2:82:E2:61:73:BC:B5:0D:66:E8:2E:11:90:97:66:92:52:62:FE:2C :6B:45:95:A1:EF:08:D6:05:C6:8E:A1\r\na=setup:actpass\r\na=rtpmap:111 opus/48000/2\r\na=extmap: 1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=extmap:2 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=rtcp-fb:111 transport-cc \r\na=msid:janus janusa0\r\na=ssrc:3479227141 cname:janus\r\na=ssrc:3479227141 msid:janus janusa0\r\na=ssrc:3479227141 mslabel:janus\r\na=ssrc:3479227141 标签:janusa0\r\nm=video 9 UDP/TLS/RTP/SAVPF 96 97\r\nc=IN IP4 185.12.12.24\r\na=sendonly\r\na=mid:video\r\na=rtcp -mux\r\na=ice-ufrag:NVx5\r\na=ice-pwd:Tz3hjJNIxACUULUbBBREaL\r\na=ice-options:trikle\r\na=指纹:sha-256 53:C2:82:E2: 61:73:BC:B5:0D:66:E8:2E:11:90:97:66:92:52:62:FE:2C:6B:45:95:A1:EF:08:D6:05: C6:8E:A1\r\na=setup:actpass\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 ccm fir\r\na=rtcp-fb:96 nack\r\ na=rtcp-fb:96 nack pli\r\na=rtcp-fb:96 goog-remb\r\na=extmap:2 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=extmap: 3A1\r\na=setup:actpass\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 ccm fir\r\na=rtcp-fb:96 nack\r\na=rtcp- fb:96 nack pli\r\na=rtcp-fb:96 goog-remb\r\na=extmap:2 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=extmap:3A1\r\na=setup:actpass\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 ccm fir\r\na=rtcp-fb:96 nack\r\na=rtcp- fb:96 nack pli\r\na=rtcp-fb:96 goog-remb\r\na=extmap:2 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=extmap:3http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:12 http://www.webrtc.org/experiments/rtp-hdrext /playout-delay\r\na=extmap:13 urn:3gpp:video-orientation\r\na=rtpmap:97 rtx/90000\r\na=fmtp:97 apt=96\r\na=ssrc-group: FID 462666388 1246572010\r\na=msid:janus janusv0\r\na=ssrc:462666388 cname:janus\r\na=ssrc:462666388 msid:janus janusv0\r\na=ssrc:462666388 mslabel:janus\r\na =ssrc:462666388 标签:janusv0\r\na=ssrc:1246572010 cname:janus\r\na=ssrc:1246572010 msid:janus janusv0\r\na=ssrc:1246572010 mslabel:janus\r\na=ssrc:1246572010 标签:janusv0\r\n"}
04:01:54 remoteFeed:[object Object]
04:01:54 创建并附加订阅者
04:01:54 创建答案
04:01:54 onSubscriberGotRemoteStream this.state=STARTING
04:01:54 视频开始
04:01:54 onSubscriberGotRemoteStream this.state=
STARTED 04:01:54 onSubscriberGotRemoteStream this.state=
STARTED 04:01:54 获得 SDP![RTCSessionDescription]
04:01:54 此 WebRTC PeerConnection 的 ICE 状态更改为检查
04:01:54 此 WebRTC PeerConnection 的 ICE 状态更改为已连接
04:01:55 onPluginMessage
04:01:55 msg:{"videoroom":" event","room":724240,"started":"ok"}
04:01:55 jsep:undefined
04:01:55 remoteFeed:[object Object]

//正常开始订阅耗时 2 秒
// 观看发布者的视频
/ / 停止上一个订阅

04:09:55 取消订阅状态=STARTED
04:09:
04:09:55 取消订阅状态=STOPPING
04:09:55 取消订阅状态=STOPPING
04:09:55 onPluginMessage
04:09:55 msg:{"videoroom":"event","room":681365,"left": “ok”}
04:09:55 jsep:undefined
04:09:55 remoteFeed:[object Object]
04:09:55 离开房间
04:09:55 onCleanUp
04:09:55 视频停止
04:09:56 关闭 PeerConnection
04:09:56 onCleanUp

// 新订阅UNNORMAL开始

04:09:58 开始请求
04:09:58 initCallback
04:09:58 createSession
04:09:58 onStreamingServerCreateSuccess
04:09:58 attachPlugin
//巨大的暂停!!!
04:10:17 附加插件
//巨大的停顿!!!
04:10:22 参与者总数=1
04:10:22 canJoin=true
04:10:22 subscriptionData={"request":"join","room":724240,"ptype":"subscriber","feed" :3606687285964170,"private_id":680291}
04:10:28 onPluginMessage
04:10:28 msg:{"videoroom":"attached","room":724240,"id":3606687285964170,"display":"724240" }
04:10:28 jsep:{"type":"offer","sdp":"v=0\r\no=- 1615780141548001 1 IN IP4 185.12.12.24\r\ns=VideoRoom 724240\r\nt=0 0\r\na=group:BUNDLE audio video\r\na=msid-semantic: WMS janus\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111\r\nc=IN IP4 185.12.12.24\r\ na=sendonly\r\na=mid:audio\r\na=rtcp-mux\r\na=ice-ufrag:WETD\r\na=ice-pwd:ch2L786bEYlY6OIi+AJ4no\r\na=ice-options:涓涓细流\r\na=指纹:sha-256 53:C2:82:E2:61:73:BC:B5:0D:66:E8:2E:11:90:97:66:92:52:62:FE :2C:6B:45:95:A1:EF:08:D6:05:C6:8E:A1\r\na=setup:actpass\r\na=rtpmap:111 opus/48000/2\r\na= extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=extmap:2 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=rtcp-fb:111 传输-cc\r\na=msid:janus janusa0\r\na=ssrc:1576733354 cname:janus\r\na=ssrc:1576733354 msid:janus janusa0\r\na=ssrc:1576733354 mslabel:janus\r\na= ssrc:1576733354 标签:janusa0\r\nm=video 9 UDP/TLS/RTP/SAVPF 96 97\r\nc=IN IP4 185.12.12.24\r\na=sendonly\r\na=mid:video\r\na=rtcp -mux\r\na=ice-ufrag:WETD\r\na=ice-pwd:ch2L786bEYlY6OIi+AJ4no\r\na=ice-options:trikle\r\na=指纹:sha-256 53:C2:82: E2:61:73:BC:B5:0D:66:E8:2E:11:90:97:66:92:52:62:FE:2C:6B:45:95:A1:EF:08:D6: 05:C6:8E:A1\r\na=setup:actpass\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 ccm fir\r\na=rtcp-fb:96 nack\ r\na=rtcp-fb:96 nack pli\r\na=rtcp-fb:96 goog-remb\r\na=extmap:2 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=外部地图:3A1\r\na=setup:actpass\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 ccm fir\r\na=rtcp-fb:96 nack\r\na=rtcp- fb:96 nack pli\r\na=rtcp-fb:96 goog-remb\r\na=extmap:2 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=extmap:3A1\r\na=setup:actpass\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 ccm fir\r\na=rtcp-fb:96 nack\r\na=rtcp- fb:96 nack pli\r\na=rtcp-fb:96 goog-remb\r\na=extmap:2 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=extmap:3http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:12 http://www.webrtc.org/experiments/rtp-hdrext /playout-delay\r\na=extmap:13 urn:3gpp:video-orientation\r\na=rtpmap:97 rtx/90000\r\na=fmtp:97 apt=96\r\na=ssrc-group: FID 4137684267 1240900261\r\na=msid:janus janusv0\r\na=ssrc:4137684267 cname:janus\r\na=ssrc:4137684267 msid:janus janusv0\r\na=ssrc:4137684267 mslabel:janus\r\na =ssrc:4137684267 标签:janusv0\r\na=ssrc:1240900261 cname:janus\r\na=ssrc:1240900261 msid:janus janusv0\r\na=ssrc:1240900261 mslabel:janus\r\na=ssrc:1240900261 标签:janusv0\r\n"}
04:10:28 remoteFeed:[object Object]
04:10:28 创建并附加订阅者
04:10:28 创建答案
04:10:28 onSubscriberGotRemoteStream this.state=STARTING
04:10:28 视频开始
04:10:28 onSubscriberGotRemoteStream this.state=
STARTED 04:10:28 onSubscriberGotRemoteStream this.state=
STARTED 04:10:28 获得 SDP![RTCSessionDescription]
04:10:28 此 WebRTC PeerConnection 的 ICE 状态更改为检查
04:10:28 此 WebRTC PeerConnection 的 ICE 状态更改为已连接
04:10:35 onSubscriberGotRemoteStream this.state=
STARTED 04:10:47 onPluginMessage
04: 10:47 msg:{"videoroom":"event","room":724240,"started":"ok"}
04:10:47 jsep:undefined
04:10:47 remoteFeed:[object Object]

// UNNORMAL开始订阅耗时 49 秒

4

0 回答 0