From c32a779825732a27f130e26bc73aa5f60566608c Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 2 May 2016 00:12:23 -0700 Subject: [PATCH] GUACAMOLE-25: Add onclose/onerror handlers to Guacamole.AudioRecorder. --- .../src/main/webapp/modules/AudioRecorder.js | 137 ++++++++++++++---- 1 file changed, 106 insertions(+), 31 deletions(-) diff --git a/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js b/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js index 43394e53f..306acbd8c 100644 --- a/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js +++ b/guacamole-common-js/src/main/webapp/modules/AudioRecorder.js @@ -29,7 +29,26 @@ var Guacamole = Guacamole || {}; */ Guacamole.AudioRecorder = function AudioRecorder() { - // AudioRecorder currently provides no functions + /** + * Callback which is invoked when the audio recording process has stopped + * and the underlying Guacamole stream has been closed normally. Audio will + * only resume recording if a new Guacamole.AudioRecorder is started. This + * Guacamole.AudioRecorder instance MAY NOT be reused. + * + * @event + */ + this.onclose = null; + + /** + * Callback which is invoked when the audio recording process cannot + * continue due to an error, if it has started at all. The underlying + * Guacamole stream is automatically closed. Future attempts to record + * audio should not be made, and this Guacamole.AudioRecorder instance + * MAY NOT be reused. + * + * @event + */ + this.onerror = null; }; @@ -114,6 +133,14 @@ Guacamole.AudioRecorder.getInstance = function getInstance(stream, mimetype) { */ Guacamole.RawAudioRecorder = function RawAudioRecorder(stream, mimetype) { + /** + * Reference to this RawAudioRecorder. + * + * @private + * @type {Guacamole.RawAudioRecorder} + */ + var recorder = this; + /** * The size of audio buffer to request from the Web Audio API when * recording or processing audio, in sample-frames. This must be a power of @@ -380,36 +407,16 @@ Guacamole.RawAudioRecorder = function RawAudioRecorder(stream, mimetype) { }; - // Once audio stream is successfully open, request and begin reading audio - writer.onack = function audioStreamAcknowledged(status) { - - // Abort stream if rejected - if (status.code !== Guacamole.Status.Code.SUCCESS) { - - // Disconnect media source node from script processor - if (source) - source.disconnect(); - - // Disconnect associated script processor node - if (processor) - processor.disconnect(); - - // Stop capture - if (mediaStream) { - var tracks = mediaStream.getTracks(); - for (var i = 0; i < tracks.length; i++) - tracks[i].stop(); - } - - // Remove references to now-unneeded components - processor = null; - source = null; - mediaStream = null; - - writer.sendEnd(); - return; - - } + /** + * Requests access to the user's microphone and begins capturing audio. All + * received audio data is resampled as necessary and forwarded to the + * Guacamole stream underlying this Guacamole.RawAudioRecorder. This + * function must be invoked ONLY ONCE per instance of + * Guacamole.RawAudioRecorder. + * + * @private + */ + var beginAudioCapture = function beginAudioCapture() { // Attempt to retrieve an audio input stream from the browser getUserMedia({ 'audio' : true }, function streamReceived(stream) { @@ -435,10 +442,78 @@ Guacamole.RawAudioRecorder = function RawAudioRecorder(stream, mimetype) { // Simply end stream if audio access is not allowed writer.sendEnd(); + // Notify of closure + if (recorder.onerror) + recorder.onerror(); + }); }; + /** + * Stops capturing audio, if the capture has started, freeing all associated + * resources. If the capture has not started, this function simply ends the + * underlying Guacamole stream. + * + * @private + */ + var stopAudioCapture = function stopAudioCapture() { + + // Disconnect media source node from script processor + if (source) + source.disconnect(); + + // Disconnect associated script processor node + if (processor) + processor.disconnect(); + + // Stop capture + if (mediaStream) { + var tracks = mediaStream.getTracks(); + for (var i = 0; i < tracks.length; i++) + tracks[i].stop(); + } + + // Remove references to now-unneeded components + processor = null; + source = null; + mediaStream = null; + + // End stream + writer.sendEnd(); + + }; + + // Once audio stream is successfully open, request and begin reading audio + writer.onack = function audioStreamAcknowledged(status) { + + // Begin capture if successful response and not yet started + if (status.code === Guacamole.Status.Code.SUCCESS && !mediaStream) + beginAudioCapture(); + + // Otherwise stop capture and cease handling any further acks + else { + + // Stop capturing audio + stopAudioCapture(); + writer.onack = null; + + // Notify if stream has closed normally + if (status.code === Guacamole.Status.Code.RESOURCE_CLOSED) { + if (recorder.onclose) + recorder.onclose(); + } + + // Otherwise notify of closure due to error + else { + if (recorder.onerror) + recorder.onerror(); + } + + } + + }; + }; Guacamole.RawAudioRecorder.prototype = new Guacamole.AudioRecorder();