audio_video/AudioVideoRecorder.js

/* globals document, window, navigator, Media, FileReader */
var FieldDBObject = require("./../FieldDBObject").FieldDBObject;
var Q = require("q");
var RecordMP3 = require("recordmp3js/js/recordmp3");

/**
 * @class AudioVideoRecorder is a minimal customization of the HTML5 media controller
 *
 * @name AudioVideoRecorder
 *
 * @extends Object
 * @constructs
 */
var AudioVideoRecorder = function AudioVideoRecorder(options) {
  if (!this._fieldDBtype) {
    this._fieldDBtype = "AudioVideoRecorder";
  }
  if (this.options) {
    console.log("AudioVideoRecorder was created with options but it doesnt accept options", options);
  }
  if (options && options.element) {
    this.element = options.element;
  }

  Object.apply(this, arguments);
};
AudioVideoRecorder.Recorder = RecordMP3.Recorder;

AudioVideoRecorder.prototype = Object.create(Object.prototype, /** @lends AudioVideoRecorder.prototype */ {
  constructor: {
    value: AudioVideoRecorder
  },

  isRecording: {
    configurable: true,
    get: function() {
      return this._isRecording;
    }
  },

  element: {
    get: function() {
      if (this.recorder) {
        return this.recorder.element;
      } else {
        return null;
      }
    },
    set: function(element) {
      if (!element) {
        console.warn("Cannot create an audio recorder, element was not passed in.");
        return;
      }
      try {
        this.recorder = new RecordMP3.Recorder({
          element: element
        });
      } catch (e) {
        console.warn("Cannot create an audio recorder.", e);
      }
    }
  },

  parent: {
    get: function() {
      if (this.recorder) {
        return this.recorder.parent;
      } else {
        return null;
      }
    },
    set: function(parent) {
      if (!parent || !this.recorder) {
        console.warn("Cannot set parent on a missing audio recorder, parent was not passed in.");
        return;
      }
      this.recorder.parent = RecordMP3.parent = parent;
    }
  },

  isPaused: {
    configurable: true,
    get: function() {
      return this._isPaused;
    }
  },

  recorderStartTime: {
    configurable: true,
    get: function() {
      if (this._recordingStartTime) {
        return this._recordingStartTime;
      } else {
        return 0;
      }
    }
  },

  isCordova: {
    configurable: true,
    get: function() {
      // return false;
      try {
        if (!Media) {
          console.log("We are most likely in Cordova, using Cordova instead of HTML5 audio");
        }
        return true;
      } catch (e) {
        console.log("We are most likely not in Cordova, using HTML5 audio", e);
        return false;
      }
    }
  },

  getDuration: {
    configurable: true,
    value: function() {
      return 0;
    }
  },

  microphoneCheck: {
    value: function() {
      this.peripheralsCheck(false);
    }
  },
  videoCheck: {
    value: function() {
      this.peripheralsCheck(true);
    }
  },
  peripheralsCheck: {
    value: function(withVideo, optionalElements) {
      var application = {},
        deferred = Q.defer(),
        self = this;

      if (FieldDBObject.application) {
        application = FieldDBObject.application;
      }
      var errorInAudioVideoPeripheralsCheck = function(error) {
        application.videoRecordingVerified = false;
        application.audioRecordingVerified = false;
        deferred.reject(error);
      };

      Q.nextTick(function() {
        if (self.peripheralsCheckRunning) {
          deferred.reject("Already running");
          return;
        }
        self.peripheralsCheckRunning = true;

        var waitUntilVideoElementIsRendered = function() {
          if (!optionalElements) {
            try {
              optionalElements = {
                image: document.getElementById("video-snapshot"),
                video: document.getElementById("video-preview"),
                audio: document.getElementById("audio-preview"),
                canvas: document.getElementById("video-snapshot-canvas"),
              };
            } catch (e) {
              this.warn("There is no DOM, cant run peripheralsCheck unless the elements are supplied ");
              this.debug(e);
            }
          }
          if (!optionalElements.canvas) {
            errorInAudioVideoPeripheralsCheck("video-snapshot-canvas is not present, cant verify peripheralsCheck");
            return;
          }
          optionalElements.canvas.width = 640;
          optionalElements.canvas.height = 360;
          var ctx = optionalElements.canvas.getContext("2d");
          var displayMediaPreview = function(localMediaStream) {
            if (localMediaStream) {
              RecordMP3.audio_context = new RecordMP3.AudioContext();
              RecordMP3.audio_source = RecordMP3.audio_context.createMediaStreamSource(localMediaStream);
            }

            if (withVideo) {
              optionalElements.video.removeAttribute("hidden");
              optionalElements.image.removeAttribute("hidden");
              optionalElements.video.removeAttribute("class");
              optionalElements.image.removeAttribute("class");
              self.type = "video";
              optionalElements.video.src = window.URL.createObjectURL(localMediaStream);
            } else {
              self.type = "audio";
              // optionalElements.audio.removeAttribute("hidden");
              // optionalElements.audio.removeAttribute("class");
              optionalElements.audio.src = window.URL.createObjectURL(localMediaStream);
            }

            var takeSnapshot = function takeSnapshot() {
              if (localMediaStream) {
                ctx.drawImage(optionalElements.video, 0, 0);
                // "image/webp" works in Chrome.
                // Other browsers will fall back to image/png.
                optionalElements.image.src = optionalElements.canvas.toDataURL("image/webp");
              }
            };
            optionalElements.video.addEventListener("click", takeSnapshot, false);

            // Note: onloadedmetadata doesn't fire in Chrome when using it with getUserMedia.
            // See crbug.com/110938.
            var onmedialoaded = function(e) {
              // Ready to go. Do some stuff.
              console.log("Video preview is working, take note of this in application so user can continue to the game.", e);
              application.videoRecordingVerified = true;
              application.audioRecordingVerified = true;
              console.log("Turning of audio feedback since confirmed that the audio works.");
              optionalElements.audio.muted = true;
              optionalElements.video.muted = true;
              navigator.geolocation.getCurrentPosition(function(position) {
                console.warn("recieved position information");
                if (FieldDBObject) {
                  FieldDBObject.software = FieldDBObject.software || {};
                  FieldDBObject.software.location = position.coords;
                }
              });

              deferred.resolve("user clicked okay");
            };
            optionalElements.audio.onloadeddata = onmedialoaded;
            optionalElements.audio.onloadedmetadata = onmedialoaded;
            optionalElements.video.onloadeddata = onmedialoaded;
            optionalElements.video.onloadedmetadata = onmedialoaded;

          };

          if (application.videoRecordingVerified) {
            displayMediaPreview();
            deferred.resolve();
            return;
          }

          /* access camera and microphone
              http://www.html5rocks.com/en/tutorials/getusermedia/intro/
           */
          navigator.getUserMedia = navigator.getUserMedia ||
            navigator.webkitGetUserMedia ||
            navigator.mozGetUserMedia ||
            navigator.msGetUserMedia;

          if (!navigator.getUserMedia) {
            errorInAudioVideoPeripheralsCheck("The Microphone/Camera is not supported in your browser.");
            return;
          }

          var errorCallback = function(e) {
            console.warn("Error in peripheralsCheck", e);
            errorInAudioVideoPeripheralsCheck("User refused access to camera and microphone!", e);
            return;
          };

          navigator.getUserMedia({
              video: {
                mandatory: {
                  maxWidth: optionalElements.canvas.width,
                  maxHeight: optionalElements.canvas.height
                }
              },
              audio: true,
              geolocation: true
            },
            displayMediaPreview,
            errorCallback
          );
        };

        if (!optionalElements.video) {
          console.warn("waiting for the video preview to get rendered, did you forget to declare it somewhere? ");
          setTimeout(waitUntilVideoElementIsRendered, 2000);
        } else {
          waitUntilVideoElementIsRendered();
        }
      });
      return deferred.promise;
    }
  },
  record: {
    configurable: true,
    value: function(optionalSource, optionalDelay) {
      console.log("todo record " + this._src + " optionalSource " + optionalSource + " optionalDelay " + optionalDelay);
    }
  },

  pause: {
    configurable: true,
    value: function() {
      console.log("todo pause recording");
    }
  },

  togglePause: {
    configurable: true,
    value: function() {
      console.log("todo toogle pause recording");
    }
  },

  stop: {
    configurable: true,
    value: function(optionalFormat) {
      var deferred = Q.defer();
      Q.nextTick(function() {
        console.log("todo stop recording", optionalFormat);
      });
      return deferred.promise;
    }
  },

  exportRecording: {
    configurable: true,
    value: function(optionalFormat) {
      var deferred = Q.defer();
      Q.nextTick(function() {
        console.log("todo export recording", optionalFormat);
      });
      return deferred.promise;
    }
  },

  /**
   * Creates an audio element and uploads the file, and makes it so you can download the file.
   * @param  {RecordMP3.Recorder} recorder Reference to the recorder object (this function is called in a cllback in the recorder)
   * @param  {Blob} mp3Data  [description]
   * @param  {DOMElement} element  [description]
   * @return {Promise}          [description]
   */
  showFile: {
    configurable: true,
    value: function(recorder, mp3Data, element) {
      var deferred = Q.defer(),
        callingContext = this;
      console.log("showing file on ", element);
      Q.nextTick(function() {
        // callingContext. = "Uploading";
        var reader = new FileReader();
        reader.onload = function(event) {
          // var fd = new FormData();
          var mp3Name = "audio_recording_" + new Date().getTime() + ".mp3";
          // var xhr = new XMLHttpRequest();
          var url = event.target.result;

          try {
            // Add audio element and download URL to page.
            var showAudioArea = document.createElement("p");
            var au = document.createElement("audio");
            au.classList = ["fielddb-audio-temp-play-audio"];
            var hf = document.createElement("a");
            hf.classList = ["fielddb-audio-temp-save-link"];
            hf.innerText = "Save to this device";
            au.controls = true;
            au.src = url;
            hf.href = url;
            hf.download = mp3Name;
            hf.innerHTML = mp3Name;
            showAudioArea.appendChild(au);
            au.play();
            showAudioArea.appendChild(hf);
            console.log("todo dont need to append this audio after upload");

            element.appendChild(showAudioArea);
            callingContext.parent.addFile({
              filename: mp3Name,
              description: "Recorded using spreadsheet app",
              data: url
            });
          } catch (e) {
            this.warn("There is no DOM, cant show audio player");
            this.debug(e);
          }
          // callingContext.status += "\nmp3name = " + mp3Name;
          // fd.append("filename", encodeURIComponent(mp3Name));

          // xhr.open("POST", new AudioVideo().BASE_SPEECH_URL + "/upload/extract/utterances", true);
          // xhr.onreadystatechange = function(response) {
          //   console.log(response);
          //   if (xhr.readyState === 4) {
          //     // callingContext.status += "\nMP3 uploaded.";
          //     // recorder.clear();

          //   }
          //   console.warn("dont clear if upload fialed");
          // };
          // xhr.send(fd);
          recorder.clear();
        };
        reader.readAsDataURL(mp3Data);

      });
      return deferred.promise;
    }
  }

});

RecordMP3.workerPath = "bower_components/recordmp3js/";
RecordMP3.showFile = AudioVideoRecorder.prototype.showFile;
try {
  RecordMP3.AudioContext = window.AudioContext;
  RecordMP3.URL = window.URL;
} catch (e) {
  console.warn("Audio recorder won't work, AudioContext is not defined", e);
}

exports.AudioVideoRecorder = AudioVideoRecorder;