瀏覽代碼

Add support for jirecon and colibri in the recording

master
hristoterezov 9 年之前
父節點
當前提交
115ce537d1
共有 6 個檔案被更改,包括 280 行新增93 行删除
  1. 2
    3
      JitsiConference.js
  2. 2
    0
      doc/API.md
  3. 132
    54
      lib-jitsi-meet.js
  4. 12
    12
      lib-jitsi-meet.min.js
  5. 5
    4
      modules/xmpp/ChatRoom.js
  6. 127
    20
      modules/xmpp/recording.js

+ 2
- 3
JitsiConference.js 查看文件

568
 
568
 
569
 /**
569
 /**
570
  * Starts/stops the recording
570
  * Starts/stops the recording
571
- * @param token a token for authentication.
572
  */
571
  */
573
-JitsiConference.prototype.toggleRecording = function (token, followEntity) {
572
+JitsiConference.prototype.toggleRecording = function (options) {
574
     if(this.room)
573
     if(this.room)
575
-        return this.room.toggleRecording(token, followEntity);
574
+        return this.room.toggleRecording(options);
576
     return new Promise(function(resolve, reject){
575
     return new Promise(function(resolve, reject){
577
         reject(new Error("The conference is not created yet!"))});
576
         reject(new Error("The conference is not created yet!"))});
578
 }
577
 }

+ 2
- 0
doc/API.md 查看文件

150
         2. resolution - the prefered resolution for the local video.
150
         2. resolution - the prefered resolution for the local video.
151
         3. openSctp - boolean property. Enables/disables datachannel support. **NOTE: we recommend to set that option to true**
151
         3. openSctp - boolean property. Enables/disables datachannel support. **NOTE: we recommend to set that option to true**
152
         4. disableAudioLevels - boolean property. Enables/disables audio levels.
152
         4. disableAudioLevels - boolean property. Enables/disables audio levels.
153
+        5. recordingType - the type of recording to be used
154
+        6. jirecon
153
 
155
 
154
 5. addEventListener(event, listener) - Subscribes the passed listener to the event.
156
 5. addEventListener(event, listener) - Subscribes the passed listener to the event.
155
     - event - one of the events from ```JitsiMeetJS.events.connection``` object.
157
     - event - one of the events from ```JitsiMeetJS.events.connection``` object.

+ 132
- 54
lib-jitsi-meet.js
文件差異過大導致無法顯示
查看文件


+ 12
- 12
lib-jitsi-meet.min.js
文件差異過大導致無法顯示
查看文件


+ 5
- 4
modules/xmpp/ChatRoom.js 查看文件

284
         if (member.isFocus) {
284
         if (member.isFocus) {
285
             this.focusMucJid = from;
285
             this.focusMucJid = from;
286
             if(!this.recording) {
286
             if(!this.recording) {
287
-                this.recording = new Recorder(this.eventEmitter, this.connection,
288
-                    this.focusMucJid);
287
+                this.recording = new Recorder(this.options.recordingType,
288
+                    this.eventEmitter, this.connection, this.focusMucJid,
289
+                    this.options.jirecon, this.roomjid);
289
                 if(this.lastJibri)
290
                 if(this.lastJibri)
290
                     this.recording.handleJibriPresence(this.lastJibri);
291
                     this.recording.handleJibriPresence(this.lastJibri);
291
             }
292
             }
698
  * Starts/stops the recording
699
  * Starts/stops the recording
699
  * @param token token for authentication
700
  * @param token token for authentication
700
  */
701
  */
701
-ChatRoom.prototype.toggleRecording = function (token, followEntity) {
702
+ChatRoom.prototype.toggleRecording = function (options) {
702
     if(this.recording)
703
     if(this.recording)
703
-        return this.recording.toggleRecording(token, followEntity);
704
+        return this.recording.toggleRecording(options);
704
 
705
 
705
     return new Promise(function(resolve, reject){
706
     return new Promise(function(resolve, reject){
706
         reject(new Error("The conference is not created yet!"))});
707
         reject(new Error("The conference is not created yet!"))});

+ 127
- 20
modules/xmpp/recording.js 查看文件

3
 var XMPPEvents = require("../../service/XMPP/XMPPEvents");
3
 var XMPPEvents = require("../../service/XMPP/XMPPEvents");
4
 var logger = require("jitsi-meet-logger").getLogger(__filename);
4
 var logger = require("jitsi-meet-logger").getLogger(__filename);
5
 
5
 
6
-function Recording(ee, connection, focusMucJid) {
7
-    this.eventEmitter = ee;
6
+function Recording(type, eventEmitter, connection, focusMucJid, jirecon,
7
+    roomjid) {
8
+    this.eventEmitter = eventEmitter;
8
     this.connection = connection;
9
     this.connection = connection;
9
     this.state = "off";
10
     this.state = "off";
10
     this.focusMucJid = focusMucJid;
11
     this.focusMucJid = focusMucJid;
12
+    this.jirecon = jirecon;
11
     this.url = null;
13
     this.url = null;
14
+    this.type = type;
12
     this._isSupported = false;
15
     this._isSupported = false;
16
+    /**
17
+     * The ID of the jirecon recording session. Jirecon generates it when we
18
+     * initially start recording, and it needs to be used in subsequent requests
19
+     * to jirecon.
20
+     */
21
+    this.jireconRid = null;
22
+    this.roomjid = roomjid;
13
 }
23
 }
14
 
24
 
25
+Recording.types = {
26
+    COLIBRI: "colibri",
27
+    JIRECON: "jirecon",
28
+    JIBRI: "jibri"
29
+};
30
+
15
 Recording.prototype.handleJibriPresence = function (jibri) {
31
 Recording.prototype.handleJibriPresence = function (jibri) {
16
     var attributes = jibri.attributes;
32
     var attributes = jibri.attributes;
17
     if(!attributes)
33
     if(!attributes)
26
     this.eventEmitter.emit(XMPPEvents.RECORDING_STATE_CHANGED);
42
     this.eventEmitter.emit(XMPPEvents.RECORDING_STATE_CHANGED);
27
 };
43
 };
28
 
44
 
29
-Recording.prototype.setRecording = function (state, streamId, followEntity,
30
-    callback, errCallback){
45
+Recording.prototype.setRecordingJibri = function (state, callback, errCallback,
46
+    options) {
31
     if (state == this.state){
47
     if (state == this.state){
32
-        return;
48
+        errCallback(new Error("Invalid state!"));
33
     }
49
     }
34
-
50
+    options = options || {};
35
     // FIXME jibri does not accept IQ without 'url' attribute set ?
51
     // FIXME jibri does not accept IQ without 'url' attribute set ?
36
 
52
 
37
     var iq = $iq({to: this.focusMucJid, type: 'set'})
53
     var iq = $iq({to: this.focusMucJid, type: 'set'})
38
         .c('jibri', {
54
         .c('jibri', {
39
             "xmlns": 'http://jitsi.org/protocol/jibri',
55
             "xmlns": 'http://jitsi.org/protocol/jibri',
40
             "action": (state === 'on') ? 'start' : 'stop',
56
             "action": (state === 'on') ? 'start' : 'stop',
41
-            "streamid": streamId,
42
-            "follow-entity": followEntity
57
+            "streamid": options.streamId,
58
+            "follow-entity": options.followEntity
43
         }).up();
59
         }).up();
44
 
60
 
45
     logger.log('Set jibri recording: '+state, iq.nodeTree);
61
     logger.log('Set jibri recording: '+state, iq.nodeTree);
56
         });
72
         });
57
 };
73
 };
58
 
74
 
59
-Recording.prototype.toggleRecording = function (token, followEntity) {
75
+Recording.prototype.setRecordingJirecon =
76
+function (state, callback, errCallback, options) {
77
+    if (state == this.state){
78
+        errCallback(new Error("Invalid state!"));
79
+    }
80
+
81
+    var iq = $iq({to: this.jirecon, type: 'set'})
82
+        .c('recording', {xmlns: 'http://jitsi.org/protocol/jirecon',
83
+            action: (state === 'on') ? 'start' : 'stop',
84
+            mucjid: this.roomjid});
85
+    if (state === 'off'){
86
+        iq.attrs({rid: this.jireconRid});
87
+    }
88
+
89
+    console.log('Start recording');
90
+    var self = this;
91
+    this.connection.sendIQ(
92
+        iq,
93
+        function (result) {
94
+            // TODO wait for an IQ with the real status, since this is
95
+            // provisional?
96
+            self.jireconRid = $(result).find('recording').attr('rid');
97
+            console.log('Recording ' +
98
+                ((state === 'on') ? 'started' : 'stopped') +
99
+                '(jirecon)' + result);
100
+            self.state = state;
101
+            if (state === 'off'){
102
+                self.jireconRid = null;
103
+            }
104
+
105
+            callback(state);
106
+        },
107
+        function (error) {
108
+            console.log('Failed to start recording, error: ', error);
109
+            errCallback(error);
110
+        });
111
+};
112
+
113
+// Sends a COLIBRI message which enables or disables (according to 'state')
114
+// the recording on the bridge. Waits for the result IQ and calls 'callback'
115
+// with the new recording state, according to the IQ.
116
+Recording.prototype.setRecordingColibri =
117
+function (state, callback, errCallback, options) {
118
+    var elem = $iq({to: this.focusMucJid, type: 'set'});
119
+    elem.c('conference', {
120
+        xmlns: 'http://jitsi.org/protocol/colibri'
121
+    });
122
+    elem.c('recording', {state: state, token: options.token});
123
+
124
+    var self = this;
125
+    this.connection.sendIQ(elem,
126
+        function (result) {
127
+            console.log('Set recording "', state, '". Result:', result);
128
+            var recordingElem = $(result).find('>conference>recording');
129
+            var newState = recordingElem.attr('state');
130
+
131
+            self.state = newState;
132
+            callback(newState);
133
+
134
+            if (newState === 'pending') {
135
+                connection.addHandler(function(iq){
136
+                    var state = $(iq).find('recording').attr('state');
137
+                    if (state) {
138
+                        self.state = newState;
139
+                        callback(state);
140
+                    }
141
+                }, 'http://jitsi.org/protocol/colibri', 'iq', null, null, null);
142
+            }
143
+        },
144
+        function (error) {
145
+            console.warn(error);
146
+            errCallback(error);
147
+        }
148
+    );
149
+};
150
+
151
+Recording.prototype.setRecording =
152
+function (state, callback, errCallback, options) {
153
+    switch(this.type){
154
+        case Recording.types.JIRECON:
155
+            this.setRecordingJirecon(state, callback, errCallback, options);
156
+            break;
157
+        case Recording.types.COLIBRI:
158
+            this.setRecordingColibri(state, callback, errCallback, options);
159
+            break;
160
+        case Recording.types.JIBRI:
161
+            this.setRecordingJibri(state, callback, errCallback, options);
162
+            break;
163
+        default:
164
+            console.error("Unknown recording type!");
165
+            return;
166
+    }
167
+};
168
+
169
+Recording.prototype.toggleRecording = function (options) {
60
     var self = this;
170
     var self = this;
61
     return new Promise(function(resolve, reject) {
171
     return new Promise(function(resolve, reject) {
62
-        if (!token) {
172
+        if ((!options.token && self.type === Recording.types.COLIBRI) ||
173
+            (!options.streamId && self.type === Recording.types.JIBRI)){
63
             reject(new Error("No token passed!"));
174
             reject(new Error("No token passed!"));
64
             logger.error("No token passed!");
175
             logger.error("No token passed!");
65
             return;
176
             return;
66
         }
177
         }
67
-        if(self.state === "on") {
68
-            reject(new Error("Recording is already started!"));
69
-            logger.error("Recording is already started!");
70
-            return;
71
-        }
72
 
178
 
73
         var oldState = self.state;
179
         var oldState = self.state;
74
         var newState = (oldState === 'off' || !oldState) ? 'on' : 'off';
180
         var newState = (oldState === 'off' || !oldState) ? 'on' : 'off';
75
 
181
 
76
         self.setRecording(newState,
182
         self.setRecording(newState,
77
-            token, followEntity,
78
             function (state, url) {
183
             function (state, url) {
79
                 logger.log("New recording state: ", state);
184
                 logger.log("New recording state: ", state);
80
                 if (state && state !== oldState) {
185
                 if (state && state !== oldState) {
186
+                    if(state !== "on" && state !== "off") {
187
+                     //state === "pending" we are waiting for the real state
188
+                        return;
189
+                    }
81
                     self.state = state;
190
                     self.state = state;
82
                     self.url = url;
191
                     self.url = url;
83
                     resolve();
192
                     resolve();
84
                 } else {
193
                 } else {
85
                     reject(new Error("State not changed!"));
194
                     reject(new Error("State not changed!"));
86
                 }
195
                 }
87
-            },
88
-            function (error) {
196
+            }, function (error) {
89
                 reject(error);
197
                 reject(error);
90
-            }
91
-        );
198
+            }, options);
92
     });
199
     });
93
 };
200
 };
94
 
201
 

Loading…
取消
儲存