Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

audioRecorder.js 9.6KB

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