فهرست منبع

Call minimal vanilla impl [wip]

Vijayakrishnan 4 سال پیش
والد
کامیت
f1d594bd09
2فایلهای تغییر یافته به همراه160 افزوده شده و 35 حذف شده
  1. 24 0
      public/css/call-minimal.css
  2. 136 35
      resources/views/app/video/call-minimal.blade.php

+ 24 - 0
public/css/call-minimal.css

@@ -0,0 +1,24 @@
+.main-view {
+
+}
+.main-view .video-view {
+    margin: 0.5rem;
+    -webkit-margin-collapse: collapse;
+}
+@media all and (min-width: 768px) {
+    .main-view {
+        display: flex;
+        flex-wrap: wrap;
+        align-items: start;
+        justify-content: center;
+    }
+    .main-view .video-view {
+        width: 480px;
+        height: 360px !important;
+    }
+}
+@media all and (max-width: 767px) {
+    #video-container {
+        padding: 0;
+    }
+}

+ 136 - 35
resources/views/app/video/call-minimal.blade.php

@@ -18,7 +18,7 @@
 
 <body class="p-0 m-0">
 
-<div class="d-flex px-3 border-bottom mb-3">
+<div class="d-flex px-3 border-bottom">
     <div class="py-2 font-weight-normal mcp-theme-1 d-inline-flex align-items-center">
         <i class="fa fa-user-injured small mr-2"></i>
         <a href="#" onclick="return window.top.openInLHS('/patients/view/{{$client->uid}}')">
@@ -51,9 +51,9 @@
         </div>
     </div>
 
-    <div id="video-container">
+    <div id="video-container" class="container">
         <div class="main-view mx-auto">
-            <div id="self-view" class="video-view"></div>
+            <div id="self-view" class="video-view" data-user-id=""></div>
         </div>
     </div>
 
@@ -120,18 +120,31 @@
             clientUid: '{{ $client->uid }}',
 
             // agora
-            mediaServiceClient: null, // set on agora init
+            mediaServiceClient: null, // instantiated on agora init
             appId: '{{ config('app.agora_appid') }}',
-            channel: '', // set on mount
-            myMicrophone: null,
-            myCamera: null,
+
+            // handle to own tracks (needed later for muting and unmuting)
+            myAudio: null,
+            myVideo: null,
 
             // sockets
             backendWsURL: 'http://localhost:8080/ws', // '{{ config('app.backend_ws_url') }}',
             socketClient: null,
 
+            // cache elements to avoid running selectors everytime
+            // Notation: $ at the beginning for jQuery objects
+            $btnStartVideo: null,
+            $videoContainer: null,
+            $selfView: null,
+
             // methods
             init: function() {
+
+                // cache elements to avoid running selectors everytime
+                this.$btnStartVideo = $('#btn-start-video');
+                this.$videoContainer = $('#video-container');
+                this.$selfView = $('#self-view');
+
                 this.registerSocket();
 
                 // event handlers
@@ -168,16 +181,22 @@
             enterRoomAsPro: function () {
                 this.ajax('/api/meeting/enterClientRoomAsPro', {clientUid: this.clientUid}, (_data) => {
                     this.getMeetingInfo(true, () => {
-                        $('#btn-start-video').prop('disabled', false);
+                        this.$btnStartVideo.prop('disabled', false);
                     });
                 });
             },
 
             // get meeting info and populate model
-            getMeetingInfo: function (_firstRun, _done) {
+            getMeetingInfo: function (_firstRun = false, _done = null) {
                 this.ajax('/api/meeting/getMyMeeting', {}, (_data) => {
-                    this.meetingData = _data.data; // fill model
-                    _done.call(this);
+
+                    // fill model
+                    this.meetingData = _data.data;
+
+                    // set own myMediaServiceIdentifier to #selfView
+                    if(_firstRun) this.$selfView.attr('data-user-id', this.meetingData.myMediaServiceIdentifier);
+
+                    if(_done) _done.call(this);
                 }, 'json');
             },
 
@@ -193,10 +212,19 @@
                         console.log('ALIX: Hurrah! Mic and camera both acquired :)');
 
                         // go to video UI
-                        $('#btn-start-video').removeClass('d-block').addClass('d-none');
-                        $('#video-container').removeClass('d-none').addClass('d-block');
-                        $('#self-view').height($('#self-view').width() * 0.75);
-                        this.myCamera.play($('#self-view')[0], {fit: 'contain'});
+                        this.$btnStartVideo.removeClass('d-block').addClass('d-none');
+                        this.$videoContainer.removeClass('d-none').addClass('d-block');
+
+                        // show own video
+                        this.$selfView.height(this.$selfView.width() * 0.75); // 4x3 mode
+                        this.myVideo.play(this.$selfView[0], {fit: 'contain'});
+
+                        // start listening to events
+                        this.initMediaEvents();
+
+                        // start publishing
+                        this.initMediaPublishing();
+
                     })
                 });
             },
@@ -206,7 +234,7 @@
                     console.log("ALIX: microphone changed!", _info.state, _info.device);
                     if(_info.state === 'ACTIVE') {
 
-                        if(!this.myMicrophone || !this.meetingData.myMedia.isMicrophoneAcquired) {
+                        if(!this.myAudio || !this.meetingData.myMedia.isMicrophoneAcquired) {
                             window.location.reload();
                         }
 
@@ -217,7 +245,7 @@
                             console.log('ALIX: (reactive) isMicrophoneOn = ', this.meetingData.myMedia.isMicrophoneOn);
 
                             // if all good, allow to proceed
-                            if(this.myMicrophone && this.meetingData.myMedia.isMicrophoneAcquired) {
+                            if(this.myAudio && this.meetingData.myMedia.isMicrophoneAcquired) {
                                 _done.call(this);
                             }
                         });
@@ -231,7 +259,7 @@
                     console.log('ALIX: (proactive) isMicrophoneOn = ', this.meetingData.myMedia.isMicrophoneOn);
 
                     // if all good, allow to proceed
-                    if(this.myMicrophone && this.meetingData.myMedia.isMicrophoneAcquired) {
+                    if(this.myAudio && this.meetingData.myMedia.isMicrophoneAcquired) {
                         _done.call(this);
                     }
                 });
@@ -240,7 +268,7 @@
             acquireMicrophone: async function() {
 
                 // if already acquired, ignore
-                if(this.myMicrophone && this.meetingData.myMedia.isMicrophoneAcquired) {
+                if(this.myAudio && this.meetingData.myMedia.isMicrophoneAcquired) {
                     console.log("ALIX: Skipping microphone acquisition..")
                     return;
                 }
@@ -248,15 +276,15 @@
                 this.meetingData.myMedia.isMicrophoneAcquired = false;
                 this.meetingData.myMedia.isMicrophoneOn = false;
                 try {
-                    this.myMicrophone = await AgoraRTC.createMicrophoneAudioTrack();
+                    this.myAudio = await AgoraRTC.createMicrophoneAudioTrack();
 
                     console.log('ALIX: acquired microphone :)');
                     this.meetingData.myMedia.isMicrophoneAcquired = true;
                     // TODO: log
                     // TODO: notify others via WS
 
-                    this.myMicrophone.setEnabled(true);
-                    this.myMicrophone.setVolume(1000); // 1000 is the max allowed value
+                    this.myAudio.setEnabled(true);
+                    this.myAudio.setVolume(1000); // 1000 is the max allowed value
                     this.meetingData.myMedia.isMicrophoneOn = true;
                     console.log('ALIX: microphone is ON and not on mute :)');
                     // TODO: log
@@ -270,7 +298,7 @@
                     // TODO: log
                     // TODO: notify others via WS
 
-                    $('#btn-start-video').removeClass('d-block').addClass('d-none');
+                    this.$btnStartVideo.removeClass('d-block').addClass('d-none');
                     // TODO: Use device/browser specific instruction & image
                     $('.mic-access-chrome-desktop').removeClass('d-none').addClass('d-block');
                 }
@@ -281,7 +309,7 @@
                     console.log("ALIX: camera changed!", _info.state, _info.device);
                     if(_info.state === 'ACTIVE') {
 
-                        if(!this.myCamera || !this.meetingData.myMedia.isCameraAcquired) {
+                        if(!this.myVideo || !this.meetingData.myMedia.isCameraAcquired) {
                             window.location.reload();
                         }
 
@@ -292,7 +320,7 @@
                             console.log('ALIX: (reactive) isCameraOn = ', this.meetingData.myMedia.isCameraOn);
 
                             // if all good, allow to proceed
-                            if(this.myCamera && this.meetingData.myMedia.isCameraAcquired) {
+                            if(this.myVideo && this.meetingData.myMedia.isCameraAcquired) {
                                 _done.call(this);
                             }
                         });
@@ -306,7 +334,7 @@
                     console.log('ALIX: (proactive) isCameraOn = ', this.meetingData.myMedia.isCameraOn);
 
                     // if all good, allow to proceed
-                    if(this.myCamera && this.meetingData.myMedia.isCameraAcquired) {
+                    if(this.myVideo && this.meetingData.myMedia.isCameraAcquired) {
                         _done.call(this);
                     }
                 });
@@ -315,7 +343,7 @@
             acquireCamera: async function() {
 
                 // if already acquired, ignore
-                if(this.myCamera && this.meetingData.myMedia.isCameraAcquired) {
+                if(this.myVideo && this.meetingData.myMedia.isCameraAcquired) {
                     console.log("ALIX: Skipping camera acquisition..")
                     return;
                 }
@@ -323,7 +351,7 @@
                 this.meetingData.myMedia.isCameraAcquired = false;
                 this.meetingData.myMedia.isCameraOn = false;
                 try {
-                    this.myCamera = await AgoraRTC.createCameraVideoTrack({
+                    this.myVideo = await AgoraRTC.createCameraVideoTrack({
                         optimizationMode: "motion"
                     });
 
@@ -332,7 +360,7 @@
                     // TODO: log
                     // TODO: notify others via WS
 
-                    this.myCamera.setEnabled(true);
+                    this.myVideo.setEnabled(true);
                     this.meetingData.myMedia.isCameraOn = true;
                     console.log('ALIX: camera is ON :)');
                     // TODO: log
@@ -346,22 +374,95 @@
                     // TODO: log
                     // TODO: notify others via WS
 
-                    $('#btn-start-video').removeClass('d-block').addClass('d-none');
+                    this.$btnStartVideo.removeClass('d-block').addClass('d-none');
                     // TODO: Use device/browser specific instruction & image
                     $('.cam-access-chrome-desktop').removeClass('d-none').addClass('d-block');
                 }
             },
 
-            // acquire own camera & mic and start publishing
-            initMediaPublishing: function() {
-
+            // join agora & start publishing
+            getMyMediaServiceToken: function(_done) {
+                $.post('/api/meeting/refreshMyMediaServiceToken', (_data) => {  // get new agora token
+                    if (!this.hasError(_data)) {
+                        this.meetingData.myMediaServiceToken = _data.data;
+                        _done.call(this);
+                    }
+                }, 'json');
             },
 
-            // init media subscriptions
-            initMediaSubscriptions: function() {
+            // init media events
+            initMediaEvents: function() {
+                this.mediaServiceClient.on('user-left', user => {
+                    // remove user's video div
+                    $('.video-view[data-user-id="' + user.uid + '"]').remove();
+
+                    // refresh state using getMyMeeting
+                    this.getMeetingInfo();
+
+                    // TODO log
+                });
+                this.mediaServiceClient.on('user-published', async (user, mediaType) => {
+                    console.log('ALIX user-published', user);
+
+                    // subscribe to the stream
+                    try {
+                        await this.mediaServiceClient.subscribe(user, mediaType);
+
+                        // subscription success, proceed to playing the stream
+                        if(mediaType === 'video') {
+                            let element = $('.video-view[data-user-id="' + user.uid + '"]');
+                            if(!element.length) {
+                                element = $('<div class="video-view" data-user-id="' + user.uid + '"></div>');
+                                element.appendTo('.main-view');
+                            }
+                            element.height(element.width() * 0.75); // 4x3 mode
+                            user.videoTrack.play(element[0], {fit: 'contain'});
+                        }
+                        else if(mediaType === 'audio') {
+                            user.audioTrack.play();
+                        }
 
+                        // TODO log
+                    }
+                    catch (e) {
+                        console.error('ALIX: could not subscribe to ' + mediaType + ' from ', user, e);
+                        // TODO log
+                    }
+                });
             },
 
+            initMediaPublishing: function() {
+
+                // get a new token
+                this.getMyMediaServiceToken(async () => {
+
+                    try {
+
+                        // join
+                        await this.mediaServiceClient.join(
+                            this.appId,
+                            this.meetingData.inMeetingForClient.clientMediaServiceRoomIdentifier,
+                            this.meetingData.myMediaServiceToken,
+                            +this.meetingData.myMediaServiceIdentifier
+                        );
+                    }
+                    catch (e) {
+                        console.error('ALIX: could not join the room!', e);
+                        // TODO log
+                    }
+
+                    try {
+                        // publish
+                        await this.mediaServiceClient.publish([this.myAudio, this.myVideo]);
+                    }
+                    catch (e) {
+                        console.error('ALIX: could not publish media to media service!', e);
+                        // TODO log
+                    }
+
+                });
+
+            },
 
             // utils - start ============================================================== //