text: /* globals window */
  
  module.exports = VideoStreamMerger
  
  function VideoStreamMerger (opts) {
    var self = this
    if (!(self instanceof VideoStreamMerger)) return new VideoStreamMerger(opts)
  
    opts = opts || {}
  
    var AudioContext = window.AudioContext || window.webkitAudioContext
    var audioSupport = !!(AudioContext && (self._audioCtx = (opts.audioContext || new AudioContext())).createMediaStreamDestination)
    var canvasSupport = !!document.createElement('canvas').captureStream
    var supported = audioSupport && canvasSupport
    if (!supported) {
      throw new Error('Unsupported browser')
    }
    self.width = opts.width || 290
    self.height = opts.height || 218
    self.fps = opts.fps || 25
    self.clearRect = opts.clearRect === undefined ? true : opts.clearRect
    self.videoElm = opts.videoElm || document.createElement('canvas')
  
    // Hidden canvas element for merging
    self._canvas = self.videoElm
    self._canvas.setAttribute('width', self.width)
    self._canvas.setAttribute('height', self.height)
    // self._canvas.setAttribute('style', 'position:fixed; left: 110%; pointer-events: none') // Push off screen
    self._ctx = self._canvas.getContext('2d')
  
    self._streams = []
  
    self._audioDestination = self._audioCtx.createMediaStreamDestination()
  
    self._setupConstantNode() // HACK for wowza #7, #10
  
    self.started = false
    self.result = null
  
    self._backgroundAudioHack()
  }
  
  VideoStreamMerger.prototype.getAudioContext = function () {
    var self = this
    return self._audioCtx
  }
  
  VideoStreamMerger.prototype.getAudioDestination = function () {
    var self = this
    return self._audioDestination
  }
  
  VideoStreamMerger.prototype.getCanvasContext = function () {
    var self = this
    return self._ctx
  }
  
  VideoStreamMerger.prototype._backgroundAudioHack = function () {
    // var self = this
    //
    // // stop browser from throttling timers by playing almost-silent audio
    // var source = self._audioCtx.createConstantSource()
    // var gainNode = self._audioCtx.createGain()
    // gainNode.gain.value = 0.001 // required to prevent popping on start
    // source.connect(gainNode)
    // gainNode.connect(self._audioCtx.destination)
    // source.start()
  }
  
  VideoStreamMerger.prototype._setupConstantNode = function () {
    // var self = this
    //
    // var constantAudioNode = self._audioCtx.createConstantSource()
    // constantAudioNode.start()
    //
    // var gain = self._audioCtx.createGain() // gain node prevents quality drop
    // gain.gain.value = 0
    //
    // constantAudioNode.connect(gain)
    // gain.connect(self._audioDestination)
  }
  
  VideoStreamMerger.prototype.updateIndex = function (mediaStream, index) {
    var self = this
  
    if (typeof mediaStream === 'string') {
      mediaStream = {
        id: mediaStream
      }
    }
  
    index = index == null ? 0 : index
  
    for (var i = 0; i < self._streams.length; i++) {
      if (mediaStream.id === self._streams[i].id) {
        self._streams[i].index = index
      }
    }
    self._sortStreams()
  }
  
  VideoStreamMerger.prototype._sortStreams = function () {
    var self = this
    self._streams = self._streams.sort((a, b) => {
      return a.index - b.index
    })
  }
  
  // convenience function for adding a media element
  VideoStreamMerger.prototype.addMediaElement = function (id, element, opts) {
    var self = this
  
    opts = opts || {}
  
    opts.x = opts.x || 0
    opts.y = opts.y || 0
    opts.width = opts.width || self.width
    opts.height = opts.height || self.height
    opts.mute = opts.mute || opts.muted || false
  
    opts.oldDraw = opts.draw
    opts.oldAudioEffect = opts.audioEffect
    if (element.tagName === 'VIDEO' || element.tagName === 'IMG') {
      opts.draw = function (ctx, _, done) {
        if (opts.oldDraw) {
          opts.oldDraw(ctx, element, done)
        } else {
          ctx.drawImage(element, opts.x, opts.y, opts.width, opts.height)
          done()
        }
      }
    } else {
      opts.draw = null
    }
  
    if (!opts.mute) {
      var audioSource = element._mediaElementSource || self.getAudioContext().createMediaElementSource(element)
      element._mediaElementSource = audioSource // can only make one source per element, so store it for later (ties the source to the element's garbage collection)
      audioSource.connect(self.getAudioContext().destination) // play audio from original element
  
      var gainNode = self.getAudioContext().createGain()
      audioSource.connect(gainNode)
      if (element.muted) {
        // keep the element "muted" while having audio on the merger
        element.muted = false
        element.volume = 0.001
        gainNode.gain.value = 1000
      } else {
        gainNode.gain.value = 1
      }
      opts.audioEffect = function (_, destination) {
        if (opts.oldAudioEffect) {
          opts.oldAudioEffect(gainNode, destination)
        } else {
          gainNode.connect(destination)
        }
      }
      opts.oldAudioEffect = null
    }
  
    self.addStream(id, opts)
  }
  
  VideoStreamMerger.prototype.addStream = function (mediaStream, opts) {
    var self = this
  
    if (typeof mediaStream === 'string') {
      return self._addData(mediaStream, opts)
    }
  
    opts = opts || {}
    var stream = {}
  
    stream.isData = false
    stream.x = opts.x || 0
    stream.y = opts.y || 0
    stream.width = opts.width || self.width
    stream.height = opts.height || self.height
    stream.draw = opts.draw || null
    stream.mute = opts.mute || opts.muted || false
    stream.audioEffect = opts.audioEffect || null
    stream.index = opts.index == null ? 0 : opts.index
    stream.hasVideo = mediaStream.getVideoTracks().length > 0
  
    // If it is the same MediaStream, we can reuse our video element (and ignore sound)
    var videoElement = null
    for (var i = 0; i < self._streams.length; i++) {
      if (self._streams[i].id === mediaStream.id) {
        videoElement = self._streams[i].element
      }
    }
    if (!videoElement) {
      videoElement = document.createElement('video')
      videoElement.autoplay = true
      videoElement.muted = true
      videoElement.setAttribute('playsinline', 'playsinline')
      videoElement.srcObject = mediaStream
      videoElement.setAttribute('style', 'position:fixed; left: 0px; top:0px; pointer-events: none; opacity:0;')
      document.body.appendChild(videoElement)
      // self.videoElm.parentNode.appendChild(self._canvas)
      // if (!stream.mute) {
      //   stream.audioSource = self._audioCtx.createMediaStreamSource(mediaStream)
      //   stream.audioOutput = self._audioCtx.createGain() // Intermediate gain node
      //   stream.audioOutput.gain.value = 1
      //   if (stream.audioEffect) {
      //     stream.audioEffect(stream.audioSource, stream.audioOutput)
      //   } else {
      //     stream.audioSource.connect(stream.audioOutput) // Default is direct connect
      //   }
      //   stream.audioOutput.connect(self._audioDestination)
      // }
    }
  
    stream.element = videoElement
    stream.id = mediaStream.id || null
    self._streams.push(stream)
    self._sortStreams()
  }
  
  VideoStreamMerger.prototype.removeStream = function (mediaStream) {
    var self = this
  
    if (typeof mediaStream === 'string') {
      mediaStream = {
        id: mediaStream
      }
    }
  
    for (var i = 0; i < self._streams.length; i++) {
      if (mediaStream.id === self._streams[i].id) {
        if (self._streams[i].audioSource) {
          self._streams[i].audioSource = null
        }
        if (self._streams[i].audioOutput) {
          self._streams[i].audioOutput.disconnect(self._audioDestination)
          self._streams[i].audioOutput = null
        }
  
        self._streams[i] = null
        self._streams.splice(i, 1)
        i--
      }
    }
  }
  
  VideoStreamMerger.prototype._addData = function (key, opts) {
    var self = this
  
    opts = opts || {}
    var stream = {}
  
    stream.isData = true
    stream.draw = opts.draw || null
    stream.audioEffect = opts.audioEffect || null
    stream.id = key
    stream.element = null
    stream.index = opts.index == null ? 0 : opts.index
  
    if (stream.audioEffect) {
      stream.audioOutput = self._audioCtx.createGain() // Intermediate gain node
      stream.audioOutput.gain.value = 1
      stream.audioEffect(null, stream.audioOutput)
      stream.audioOutput.connect(self._audioDestination)
    }
  
    self._streams.push(stream)
    self._sortStreams()
  }
  
  // Wrapper around requestAnimationFrame and setTimeout to avoid background throttling
  VideoStreamMerger.prototype._requestAnimationFrame = function (callback) {
    if (document.hidden) {
      setTimeout(callback, 0)
    } else {
      requestAnimationFrame(callback)
    }
  }
  
  VideoStreamMerger.prototype.start = function () {
    var self = this
  
    self.started = true
    self._requestAnimationFrame(self._draw.bind(self))
    // Add video
    self.result = self._canvas.captureStream(self.fps)
  
    // Remove "dead" audio track
    var deadTrack = self.result.getAudioTracks()[0]
    if (deadTrack) self.result.removeTrack(deadTrack)
  
    // Add audio
    var audioTracks = self._audioDestination.stream.getAudioTracks()
    self.result.addTrack(audioTracks[0])
  }
  
  VideoStreamMerger.prototype._draw = function () {
    var self = this
    if (!self.started) return
    var awaiting = self._streams.length
    function done () {
      awaiting--
      if (awaiting <= 0) self._requestAnimationFrame(self._draw.bind(self))
    }
  
    if (self.clearRect) {
      self._ctx.clearRect(0, 0, self.width, self.height)
    }
    self._streams.forEach(function (video) {
      if (video.draw) { // custom frame transform
        video.draw(self._ctx, video.element, done)
      } else if (!video.isData && video.hasVideo) {
        self._ctx.drawImage(video.element, video.x, video.y, video.width, video.height)
        done()
      } else {
        done()
      }
    })
  
    if (self._streams.length === 0) done()
  }
  
  VideoStreamMerger.prototype.destroy = function () {
    var self = this
  
    self.started = false
  
    self._canvas = null
    self._ctx = null
    self._streams = []
    self._audioCtx.close()
    self._audioCtx = null
    self._audioDestination = null
  
    self.result.getTracks().forEach(function (t) {
      t.stop()
    })
    self.result = null
  }
  
  module.exports = VideoStreamMerger
  
  
  
  //////////////////
  // WEBPACK FOOTER
  // ./node_modules/video-stream-merger/src/index.js
  // module id = 679
  // module chunks = 0
  