Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

audioRecorder.js 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. import RecordingResult from './recordingResult';
  2. import TrackRecorder from './trackRecorder';
  3. /**
  4. * Possible audio formats MIME types
  5. */
  6. const AUDIO_WEBM = 'audio/webm'; // Supported in chrome
  7. const AUDIO_OGG = 'audio/ogg'; // Supported in firefox
  8. /**
  9. * Starts the recording of a JitsiTrack in a TrackRecorder object.
  10. * This will also define the timestamp and try to update the name
  11. * @param trackRecorder the TrackRecorder to start
  12. */
  13. function startRecorder(trackRecorder) {
  14. if (trackRecorder.recorder === undefined) {
  15. throw new Error('Passed an object to startRecorder which is not a '
  16. + 'TrackRecorder object');
  17. }
  18. trackRecorder.recorder.start();
  19. trackRecorder.startTime = new Date();
  20. }
  21. /**
  22. * Stops the recording of a JitsiTrack in a TrackRecorder object.
  23. * This will also try to update the name
  24. * @param trackRecorder the TrackRecorder to stop
  25. */
  26. function stopRecorder(trackRecorder) {
  27. if (trackRecorder.recorder === undefined) {
  28. throw new Error('Passed an object to stopRecorder which is not a '
  29. + 'TrackRecorder object');
  30. }
  31. trackRecorder.recorder.stop();
  32. }
  33. /**
  34. * Determines which kind of audio recording the browser supports
  35. * chrome supports "audio/webm" and firefox supports "audio/ogg"
  36. */
  37. function determineCorrectFileType() {
  38. if (MediaRecorder.isTypeSupported(AUDIO_WEBM)) {
  39. return AUDIO_WEBM;
  40. } else if (MediaRecorder.isTypeSupported(AUDIO_OGG)) {
  41. return AUDIO_OGG;
  42. }
  43. throw new Error(
  44. 'unable to create a MediaRecorder with the right mimetype!');
  45. }
  46. /**
  47. * main exported object of the file, holding all
  48. * relevant functions and variables for the outside world
  49. * @param jitsiConference the jitsiConference which this object
  50. * is going to record
  51. */
  52. function AudioRecorder(jitsiConference) {
  53. // array of TrackRecorders, where each trackRecorder
  54. // holds the JitsiTrack, MediaRecorder and recorder data
  55. this.recorders = [];
  56. // get which file type is supported by the current browser
  57. this.fileType = determineCorrectFileType();
  58. // boolean flag for active recording
  59. this.isRecording = false;
  60. // the jitsiconference the object is recording
  61. this.jitsiConference = jitsiConference;
  62. }
  63. /**
  64. * Add the exported module so that it can be accessed by other files
  65. */
  66. AudioRecorder.determineCorrectFileType = determineCorrectFileType;
  67. /**
  68. * Adds a new TrackRecorder object to the array.
  69. *
  70. * @param track the track potentially holding an audio stream
  71. */
  72. AudioRecorder.prototype.addTrack = function(track) {
  73. if (track.isAudioTrack()) {
  74. // create the track recorder
  75. const trackRecorder = this.instantiateTrackRecorder(track);
  76. // push it to the local array of all recorders
  77. this.recorders.push(trackRecorder);
  78. // update the name of the trackRecorders
  79. this.updateNames();
  80. // If we're already recording, immediately start recording this new
  81. // track.
  82. if (this.isRecording) {
  83. startRecorder(trackRecorder);
  84. }
  85. }
  86. };
  87. /**
  88. * Creates a TrackRecorder object. Also creates the MediaRecorder and
  89. * data array for the trackRecorder.
  90. * @param track the JitsiTrack holding the audio MediaStream(s)
  91. */
  92. AudioRecorder.prototype.instantiateTrackRecorder = function(track) {
  93. const trackRecorder = new TrackRecorder(track);
  94. // Create a new stream which only holds the audio track
  95. const originalStream = trackRecorder.track.getOriginalStream();
  96. const stream = new MediaStream();
  97. originalStream.getAudioTracks().forEach(t => stream.addTrack(t));
  98. // Create the MediaRecorder
  99. trackRecorder.recorder = new MediaRecorder(stream,
  100. { mimeType: this.fileType });
  101. // array for holding the recorder data. Resets it when
  102. // audio already has been recorder once
  103. trackRecorder.data = [];
  104. // function handling a dataEvent, e.g the stream gets new data
  105. trackRecorder.recorder.ondataavailable = function(dataEvent) {
  106. if (dataEvent.data.size > 0) {
  107. trackRecorder.data.push(dataEvent.data);
  108. }
  109. };
  110. return trackRecorder;
  111. };
  112. /**
  113. * Notifies the module that a specific track has stopped, e.g participant left
  114. * the conference.
  115. * if the recording has not started yet, the TrackRecorder will be removed from
  116. * the array. If the recording has started, the recorder will stop recording
  117. * but not removed from the array so that the recorded stream can still be
  118. * accessed
  119. *
  120. * @param {JitsiTrack} track the JitsiTrack to remove from the recording session
  121. */
  122. AudioRecorder.prototype.removeTrack = function(track) {
  123. if (track.isVideoTrack()) {
  124. return;
  125. }
  126. const array = this.recorders;
  127. let i;
  128. for (i = 0; i < array.length; i++) {
  129. if (array[i].track.getParticipantId() === track.getParticipantId()) {
  130. const recorderToRemove = array[i];
  131. if (this.isRecording) {
  132. stopRecorder(recorderToRemove);
  133. } else {
  134. // remove the TrackRecorder from the array
  135. array.splice(i, 1);
  136. }
  137. }
  138. }
  139. // make sure the names are up to date
  140. this.updateNames();
  141. };
  142. /**
  143. * Tries to update the name value of all TrackRecorder in the array.
  144. * If it hasn't changed,it will keep the exiting name. If it changes to a
  145. * undefined value, the old value will also be kept.
  146. */
  147. AudioRecorder.prototype.updateNames = function() {
  148. const conference = this.jitsiConference;
  149. this.recorders.forEach(trackRecorder => {
  150. if (trackRecorder.track.isLocal()) {
  151. trackRecorder.name = 'the transcriber';
  152. } else {
  153. const id = trackRecorder.track.getParticipantId();
  154. const participant = conference.getParticipantById(id);
  155. const newName = participant.getDisplayName();
  156. if (newName !== 'undefined') {
  157. trackRecorder.name = newName;
  158. }
  159. }
  160. });
  161. };
  162. /**
  163. * Starts the audio recording of every local and remote track
  164. */
  165. AudioRecorder.prototype.start = function() {
  166. if (this.isRecording) {
  167. throw new Error('audiorecorder is already recording');
  168. }
  169. // set boolean isRecording flag to true so if new participants join the
  170. // conference, that track can instantly start recording as well
  171. this.isRecording = true;
  172. // start all the mediaRecorders
  173. this.recorders.forEach(trackRecorder => startRecorder(trackRecorder));
  174. // log that recording has started
  175. console.log(
  176. `Started the recording of the audio. There are currently ${
  177. this.recorders.length} recorders active.`);
  178. };
  179. /**
  180. * Stops the audio recording of every local and remote track
  181. */
  182. AudioRecorder.prototype.stop = function() {
  183. // set the boolean flag to false
  184. this.isRecording = false;
  185. // stop all recorders
  186. this.recorders.forEach(trackRecorder => stopRecorder(trackRecorder));
  187. console.log('stopped recording');
  188. };
  189. /**
  190. * link hacking to download all recorded audio streams
  191. */
  192. AudioRecorder.prototype.download = function() {
  193. this.recorders.forEach(trackRecorder => {
  194. const blob = new Blob(trackRecorder.data, { type: this.fileType });
  195. const url = URL.createObjectURL(blob);
  196. const a = document.createElement('a');
  197. document.body.appendChild(a);
  198. a.style = 'display: none';
  199. a.href = url;
  200. a.download = `test.${this.fileType.split('/')[1]}`;
  201. a.click();
  202. window.URL.revokeObjectURL(url);
  203. });
  204. };
  205. /**
  206. * returns the audio files of all recorders as an array of objects,
  207. * which include the name of the owner of the track and the starting time stamp
  208. * @returns {Array} an array of RecordingResult objects
  209. */
  210. AudioRecorder.prototype.getRecordingResults = function() {
  211. if (this.isRecording) {
  212. throw new Error(
  213. 'cannot get blobs because the AudioRecorder is still recording!');
  214. }
  215. // make sure the names are up to date before sending them off
  216. this.updateNames();
  217. const array = [];
  218. this.recorders.forEach(
  219. recorder =>
  220. array.push(
  221. new RecordingResult(
  222. new Blob(recorder.data, { type: this.fileType }),
  223. recorder.name,
  224. recorder.startTime)));
  225. return array;
  226. };
  227. /**
  228. * Gets the mime type of the recorder audio
  229. * @returns {String} the mime type of the recorder audio
  230. */
  231. AudioRecorder.prototype.getFileType = function() {
  232. return this.fileType;
  233. };
  234. /**
  235. * export the main object AudioRecorder
  236. */
  237. export default AudioRecorder;