diff --git a/src/js/tech/html5.js b/src/js/tech/html5.js
index d860d4a24..587b62f9e 100644
--- a/src/js/tech/html5.js
+++ b/src/js/tech/html5.js
@@ -919,13 +919,15 @@ Html5.canControlPlaybackRate = function() {
  *         - False otherwise
  */
 Html5.canOverrideAttributes = function() {
-  // if we cannot overwrite the src property, there is no support
+  // if we cannot overwrite the src/innerHTML property, there is no support
   // iOS 7 safari for instance cannot do this.
   try {
     const noop = () => {};
 
     Object.defineProperty(document.createElement('video'), 'src', {get: noop, set: noop});
     Object.defineProperty(document.createElement('audio'), 'src', {get: noop, set: noop});
+    Object.defineProperty(document.createElement('video'), 'innerHTML', {get: noop, set: noop});
+    Object.defineProperty(document.createElement('audio'), 'innerHTML', {get: noop, set: noop});
   } catch (e) {
     return false;
   }
diff --git a/src/js/tech/setup-sourceset.js b/src/js/tech/setup-sourceset.js
index 5bbd54c0a..73ad56488 100644
--- a/src/js/tech/setup-sourceset.js
+++ b/src/js/tech/setup-sourceset.js
@@ -1,21 +1,152 @@
 import window from 'global/window';
+import document from 'global/document';
 import mergeOptions from '../utils/merge-options';
 
-const setupSourceset = function(tech) {
+/**
+ * This function is used to fire a sourceset when there is something
+ * similar to `mediaEl.load()` being called. It will try to find the source via
+ * the `src` attribute and then the `<source>` elements. It will then fire `sourceset`
+ * with the source that was found or empty string if we cannot know. If it cannot
+ * find a source then `sourceset` will not be fired.
+ *
+ * @param {Html5} tech
+ *        The tech object that sourceset was setup on
+ *
+ * @return {boolean}
+ *         returns false if the sourceset was not fired and true otherwise.
+ */
+const sourcesetLoad = (tech) => {
+  const el = tech.el();
 
-  if (!tech.featuresSourceset) {
+  // if `el.src` is set, that source will be loaded.
+  if (el.src) {
+    tech.triggerSourceset(el.src);
+    return true;
+  }
+
+  /**
+   * Since there isn't a src property on the media element, source elements will be used for
+   * implementing the source selection algorithm. This happens asynchronously and
+   * for most cases were there is more than one source we cannot tell what source will
+   * be loaded, without re-implementing the source selection algorithm. At this time we are not
+   * going to do that. There are three special cases that we do handle here though:
+   *
+   * 1. If there are no sources, do not fire `sourceset`.
+   * 2. If there is only one `<source>` with a `src` property/attribute that is our `src`
+   * 3. If there is more than one `<source>` but all of them have the same `src` url.
+   *    That will be our src.
+   */
+  const sources = tech.$$('source');
+  const srcUrls = [];
+  let src = '';
+
+  // if there are no sources, do not fire sourceset
+  if (!sources.length) {
+    return false;
+  }
+
+  // only count valid/non-duplicate source elements
+  for (let i = 0; i < sources.length; i++) {
+    const url = sources[i].src;
+
+    if (url && srcUrls.indexOf(url) === -1) {
+      srcUrls.push(url);
+    }
+  }
+
+  // there were no valid sources
+  if (!srcUrls.length) {
     return;
   }
 
-  const el = tech.el();
-
-  // we need to fire sourceset when the player is ready
-  // if we find that the media element had a src when it was
-  // given to us and that tech element is not in a stalled state
-  if (el.src || el.currentSrc && tech.el().initNetworkState_ !== 3) {
-    tech.triggerSourceset(el.src || el.currentSrc);
+  // there is only one valid source element url
+  // use that
+  if (srcUrls.length === 1) {
+    src = srcUrls[0];
   }
 
+  tech.triggerSourceset(src);
+  return true;
+};
+
+/**
+ * Get the browsers property descriptor for the `innerHTML`
+ * property. This will allow us to overwrite it without
+ * destroying native functionality.
+ *
+ * @param {HTMLMediaElement} el
+ *        The tech element that should be used to get the descriptor
+ *
+ * @return {Object}
+ *          The property descriptor for innerHTML.
+ */
+const getInnerHTMLDescriptor = (el) => {
+  const proto = window.Element.prototype;
+  let innerDescriptor = {};
+
+  // preserve getters/setters already on `el.innerHTML` if they exist
+  if (Object.getOwnPropertyDescriptor(el, 'innerHTML')) {
+    innerDescriptor = Object.getOwnPropertyDescriptor(el, 'innerHTML');
+  } else if (Object.getOwnPropertyDescriptor(proto, 'innerHTML')) {
+    innerDescriptor = Object.getOwnPropertyDescriptor(proto, 'innerHTML');
+  }
+
+  if (!innerDescriptor.get) {
+    innerDescriptor.get = function() {
+      return el.cloneNode().innerHTML;
+    };
+  }
+
+  if (!innerDescriptor.set) {
+    innerDescriptor.set = function(v) {
+      // remove all current content from inside
+      el.innerText = '';
+
+      // make a dummy node to use innerHTML on
+      const dummy = document.createElement(el.nodeName.toLowerCase());
+
+      // set innerHTML to the value provided
+      dummy.innerHTML = v;
+
+      // make a document fragment to hold the nodes from dummy
+      const docFrag = document.createDocumentFragment();
+
+      // copy all of the nodes created by the innerHTML on dummy
+      // to the document fragment
+      while (dummy.childNodes.length) {
+        docFrag.appendChild(dummy.childNodes[0]);
+      }
+
+      // now we add all of that html in one by appending the
+      // document fragment. This is how innerHTML does it.
+      window.Element.prototype.appendChild.call(el, docFrag);
+
+      // then return the result that innerHTML's setter would
+      return el.innerHTML;
+    };
+  }
+
+  if (typeof innerDescriptor.enumerable === 'undefined') {
+    innerDescriptor.enumerable = true;
+  }
+
+  innerDescriptor.configurable = true;
+
+  return innerDescriptor;
+};
+
+/**
+ * Get the browsers property descriptor for the `src`
+ * property. This will allow us to overwrite it without
+ * destroying native functionality.
+ *
+ * @param {HTMLMediaElement} el
+ *        The tech element that should be used to get the descriptor
+ *
+ * @return {Object}
+ *          The property descriptor for `src`.
+ */
+const getSrcDescriptor = (el) => {
   const proto = window.HTMLMediaElement.prototype;
   let srcDescriptor = {};
 
@@ -42,12 +173,158 @@ const setupSourceset = function(tech) {
     srcDescriptor.enumerable = true;
   }
 
+  srcDescriptor.configurable = true;
+
+  return srcDescriptor;
+};
+
+/**
+ * Patches browser internal functions so that we can tell syncronously
+ * if a `<source>` was appended to the media element. For some reason this
+ * causes a `sourceset` if the the media element is ready and has no source.
+ * This happens when:
+ * - The page has just loaded and the media element does not have a source.
+ * - The media element was emptied of all sources, then `load()` was called.
+ *
+ * It does this by patching the following functions/properties when they are supported:
+ *
+ * - `append()` - can be used to add a `<source>` element to the media element
+ * - `appendChild()` - can be used to add a `<source>` element to the media element
+ * - `insertAdjacentHTML()` -  can be used to add a `<source>` element to the media element
+ * - `innerHTML` -  can be used to add a `<source>` element to the media element
+ *
+ * @param {Html5} tech
+ *        The tech object that sourceset is being setup on.
+ */
+const firstSourceWatch = function(tech) {
+  const el = tech.el();
+
+  // make sure firstSourceWatch isn't setup twice.
+  if (el.firstSourceWatch_) {
+    return;
+  }
+
+  el.firstSourceWatch_ = true;
+  const oldAppend = el.append;
+  const oldAppendChild = el.appendChild;
+  const oldInsertAdjacentHTML = el.insertAdjacentHTML;
+  const innerDescriptor = getInnerHTMLDescriptor(el);
+
+  el.appendChild = function() {
+    const retval = oldAppendChild.apply(el, arguments);
+
+    sourcesetLoad(tech);
+
+    return retval;
+  };
+
+  if (oldAppend) {
+    el.append = function() {
+      const retval = oldAppend.apply(el, arguments);
+
+      sourcesetLoad(tech);
+
+      return retval;
+    };
+  }
+
+  if (oldInsertAdjacentHTML) {
+    el.insertAdjacentHTML = function() {
+      const retval = oldInsertAdjacentHTML.apply(el, arguments);
+
+      sourcesetLoad(tech);
+
+      return retval;
+    };
+  }
+
+  Object.defineProperty(el, 'innerHTML', {
+    get: innerDescriptor.get.bind(el),
+    set(v) {
+      const retval = innerDescriptor.set.call(el, v);
+
+      sourcesetLoad(tech);
+
+      return retval;
+    },
+    configurable: true,
+    enumerable: innerDescriptor.enumerable
+  });
+
+  // on the first sourceset, we need to revert
+  // our changes
+  tech.one('sourceset', (e) => {
+    el.firstSourceWatch_ = false;
+    el.appendChild = oldAppendChild;
+
+    if (oldAppend) {
+      el.append = oldAppend;
+    }
+    if (oldInsertAdjacentHTML) {
+      el.insertAdjacentHTML = oldInsertAdjacentHTML;
+    }
+
+    Object.defineProperty(el, 'innerHTML', innerDescriptor);
+  });
+};
+
+/**
+ * setup `sourceset` handling on the `Html5` tech. This function
+ * patches the following element properties/functions:
+ *
+ * - `src` - to determine when `src` is set
+ * - `setAttribute()` - to determine when `src` is set
+ * - `load()` - this re-triggers the source selection algorithm, and can
+ *              cause a sourceset.
+ *
+ * If there is no source when we are adding `sourceset` support or during a `load()`
+ * we also patch the functions listed in `firstSourceWatch`.
+ *
+ * @param {Html5} tech
+ *        The tech to patch
+ */
+const setupSourceset = function(tech) {
+  if (!tech.featuresSourceset) {
+    return;
+  }
+
+  const el = tech.el();
+
+  // make sure sourceset isn't setup twice.
+  if (el.setupSourceset_) {
+    return;
+  }
+
+  el.setupSourceset_ = true;
+
+  const srcDescriptor = getSrcDescriptor(el);
+  const oldSetAttribute = el.setAttribute;
+  const oldLoad = el.load;
+
+  // we need to fire sourceset when the player is ready
+  // if we find that the media element had a src when it was
+  // given to us and that tech element is not in a stalled state
+  if (el.src || el.currentSrc && el.initNetworkState_ !== 3) {
+    if (el.currentSrc) {
+      tech.triggerSourceset(el.currentSrc);
+    } else {
+      sourcesetLoad(tech);
+    }
+  }
+
+  // for some reason adding a source element when a mediaElement has no source
+  // calls `load` internally right away. We need to handle that.
+  if (!el.src && !el.currentSrc && !tech.$$('source').length) {
+    firstSourceWatch(tech);
+  }
+
   Object.defineProperty(el, 'src', {
     get: srcDescriptor.get.bind(el),
     set: (v) => {
       const retval = srcDescriptor.set.call(el, v);
 
-      tech.triggerSourceset(v);
+      // we use the getter here to get the actual value set on src
+      tech.triggerSourceset(el.src);
 
       return retval;
     },
@@ -55,29 +332,26 @@ const setupSourceset = function(tech) {
     enumerable: srcDescriptor.enumerable
   });
 
-  const oldSetAttribute = el.setAttribute;
-
   el.setAttribute = (n, v) => {
     const retval = oldSetAttribute.call(el, n, v);
 
     if (n === 'src') {
-      tech.triggerSourceset(v);
+      tech.triggerSourceset(el.getAttribute('src'));
     }
 
     return retval;
   };
 
-  const oldLoad = el.load;
-
   el.load = () => {
     const retval = oldLoad.call(el);
 
-    // if `el.src` is set, that source will be loaded
-    // otherwise, we can't know for sure what source will be set because
-    // source elements will be used but implementing the source selection algorithm
-    // is laborious and asynchronous, so,
-    // instead return an empty string to basically indicate source may change
-    tech.triggerSourceset(el.src || '');
+    // if load was called, but there was no source to fire
+    // sourceset on. We have to watch for a source append
+    // as that can trigger a `sourceset` when the media element
+    // has no source
+    if (!sourcesetLoad(tech)) {
+      firstSourceWatch(tech);
+    }
 
     return retval;
   };
diff --git a/test/unit/sourceset.test.js b/test/unit/sourceset.test.js
index d2ed1d2ed..ad2d7385f 100644
--- a/test/unit/sourceset.test.js
+++ b/test/unit/sourceset.test.js
@@ -161,51 +161,6 @@ QUnit[qunitFn]('sourceset', function(hooks) {
       });
     });
 
-    QUnit.test('player.src({...}) one source', function(assert) {
-      const done = assert.async();
-
-      this.player = videojs(this.mediaEl, {
-        enableSourceset: true
-      });
-      this.player.one('sourceset', () => {
-        validateSource(assert, this.player, [this.testSrc]);
-        done();
-      });
-
-      this.player.src(this.testSrc);
-    });
-
-    QUnit.test('player.src({...}) preload auto', function(assert) {
-      const done = assert.async();
-
-      this.mediaEl.setAttribute('preload', 'auto');
-      this.player = videojs(this.mediaEl, {
-        enableSourceset: true
-      });
-
-      this.player.one('sourceset', () => {
-        validateSource(assert, this.player, [this.testSrc]);
-        done();
-      });
-
-      this.player.src(this.testSrc);
-    });
-
-    QUnit.test('player.src({...}) two sources', function(assert) {
-      const done = assert.async();
-
-      this.player = videojs(this.mediaEl, {
-        enableSourceset: true
-      });
-
-      this.player.one('sourceset', () => {
-        validateSource(assert, this.player, [this.sourceOne, this.sourceTwo]);
-        done();
-      });
-
-      this.player.src([this.sourceOne, this.sourceTwo]);
-    });
-
     QUnit.test('mediaEl.src = ...;', function(assert) {
       const done = assert.async();
 
@@ -292,6 +247,320 @@ QUnit[qunitFn]('sourceset', function(hooks) {
     });
   }));
 
+  QUnit.module('source after player', (subhooks) => testTypes.forEach((testName) => {
+    QUnit.module(testName, {
+      beforeEach() {
+        sinon.stub(log, 'error');
+
+        setupEnv(this, testName);
+      },
+      afterEach: setupAfterEach(1)
+    });
+
+    QUnit.test('player.src({...}) one source', function(assert) {
+      const done = assert.async();
+
+      this.player = videojs(this.mediaEl, {
+        enableSourceset: true
+      });
+      this.player.one('sourceset', () => {
+        validateSource(assert, this.player, [this.testSrc]);
+        done();
+      });
+
+      this.player.src(this.testSrc);
+    });
+
+    QUnit.test('player.src({...}) preload auto', function(assert) {
+      const done = assert.async();
+
+      this.mediaEl.setAttribute('preload', 'auto');
+      this.player = videojs(this.mediaEl, {
+        enableSourceset: true
+      });
+
+      this.player.one('sourceset', () => {
+        validateSource(assert, this.player, [this.testSrc]);
+        done();
+      });
+
+      this.player.src(this.testSrc);
+    });
+
+    QUnit.test('player.src({...}) two sources', function(assert) {
+      const done = assert.async();
+
+      this.player = videojs(this.mediaEl, {
+        enableSourceset: true
+      });
+
+      this.player.one('sourceset', () => {
+        validateSource(assert, this.player, [this.sourceOne, this.sourceTwo]);
+        done();
+      });
+
+      this.player.src([this.sourceOne, this.sourceTwo]);
+    });
+
+    QUnit.test('mediaEl.src = ...;', function(assert) {
+      const done = assert.async();
+
+      this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+      this.player.one('sourceset', (e) => {
+        validateSource(assert, this.player, [this.testSrc]);
+        done();
+      });
+
+      this.player.tech_.el_.src = this.testSrc.src;
+    });
+
+    QUnit.test('mediaEl.setAttribute("src", ...)"', function(assert) {
+      const done = assert.async();
+
+      this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+      this.player.one('sourceset', (e) => {
+        validateSource(assert, this.player, [this.testSrc]);
+        done();
+      });
+
+      this.player.tech_.el_.setAttribute('src', this.testSrc.src);
+    });
+
+    const appendTypes = [
+      {name: 'appendChild', fn: (el, obj) => el.appendChild(obj)},
+      {name: 'innerHTML', fn: (el, obj) => {el.innerHTML = obj.outerHTML;}}, // eslint-disable-line
+    ];
+
+    // ie does not support this and safari < 10 does not either
+    if (window.Element.prototype.append) {
+      appendTypes.push({name: 'append', fn: (el, obj) => el.append(obj)});
+    }
+
+    if (window.Element.prototype.insertAdjacentHTML) {
+      appendTypes.push({name: 'insertAdjacentHTML', fn: (el, obj) => el.insertAdjacentHTML('afterbegin', obj.outerHTML)});
+    }
+
+    appendTypes.forEach((appendObj) => {
+
+      QUnit.test(`<source> one source through ${appendObj.name}`, function(assert) {
+        const done = assert.async();
+
+        this.source = document.createElement('source');
+        this.source.src = this.testSrc.src;
+        this.source.type = this.testSrc.type;
+
+        this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+        this.player.one('sourceset', (e) => {
+          assert.equal(e.src, this.testSrc.src, 'source is as expected');
+          done();
+        });
+
+        // since the media el has no source, just appending will
+        // change the source without calling load
+        appendObj.fn(this.player.tech_.el_, this.source);
+      });
+
+      QUnit.test(`<source> one source through ${appendObj.name} and load`, function(assert) {
+        const done = assert.async();
+
+        this.totalSourcesets = 2;
+        this.source = document.createElement('source');
+        this.source.src = this.testSrc.src;
+        this.source.type = this.testSrc.type;
+
+        this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+        this.player.one('sourceset', (e1) => {
+          assert.equal(e1.src, this.testSrc.src, 'event has expected source');
+
+          this.player.one('sourceset', (e2) => {
+            assert.equal(e2.src, this.testSrc.src, 'second event has expected source');
+            done();
+          });
+        });
+
+        // since the media el has no source, just appending will
+        // change the source without calling load
+        appendObj.fn(this.player.tech_.el_, this.source);
+
+        // should fire an additional sourceset
+        this.player.tech_.el_.load();
+      });
+
+      QUnit.test(`one <source> through ${appendObj.name} and then mediaEl.src`, function(assert) {
+        const done = assert.async();
+
+        this.totalSourcesets = 2;
+        this.source = document.createElement('source');
+        this.source.src = this.testSrc.src;
+        this.source.type = this.testSrc.type;
+
+        this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+        this.player.one('sourceset', (e) => {
+          assert.equal(e.src, this.testSrc.src, 'source is as expected');
+
+          this.player.one('sourceset', (e2) => {
+            validateSource(assert, this.player, [this.sourceOne]);
+
+            done();
+          });
+        });
+
+        // since the media el has no source, just appending will
+        // change the source without calling load
+        appendObj.fn(this.player.tech_.el_, this.source);
+
+        // should fire an additional sourceset
+        this.player.tech_.el_.src = this.sourceOne.src;
+      });
+
+      QUnit.test(`one <source> through ${appendObj.name} and then mediaEl.setAttribute`, function(assert) {
+        const done = assert.async();
+
+        this.totalSourcesets = 2;
+        this.source = document.createElement('source');
+        this.source.src = this.testSrc.src;
+        this.source.type = this.testSrc.type;
+
+        this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+        this.player.one('sourceset', (e) => {
+          assert.equal(e.src, this.testSrc.src, 'source is as expected');
+
+          this.player.one('sourceset', (e2) => {
+            validateSource(assert, this.player, [this.sourceOne]);
+
+            done();
+          });
+        });
+
+        // since the media el has no source, just appending will
+        // change the source without calling load
+        appendObj.fn(this.player.tech_.el_, this.source);
+
+        // should fire an additional sourceset
+        this.player.tech_.el_.setAttribute('src', this.sourceOne.src);
+      });
+
+      QUnit.test(`mediaEl.src and then <source> through ${appendObj.name}`, function(assert) {
+        const done = assert.async();
+
+        this.source = document.createElement('source');
+        this.source.src = this.testSrc.src;
+        this.source.type = this.testSrc.type;
+
+        this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+        this.player.one('sourceset', (e) => {
+          validateSource(assert, this.player, [this.sourceOne]);
+
+          done();
+        });
+
+        this.player.tech_.el_.src = this.sourceOne.src;
+
+        // should not fire sourceset
+        appendObj.fn(this.player.tech_.el_, this.source);
+      });
+
+      QUnit.test(`mediaEl.setAttribute and then <source> through ${appendObj.name}`, function(assert) {
+        const done = assert.async();
+
+        this.source = document.createElement('source');
+        this.source.src = this.testSrc.src;
+        this.source.type = this.testSrc.type;
+
+        this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+        this.player.one('sourceset', (e) => {
+          validateSource(assert, this.player, [this.sourceOne]);
+
+          done();
+        });
+
+        this.player.tech_.el_.setAttribute('src', this.sourceOne.src);
+
+        // should not fire sourceset
+        appendObj.fn(this.player.tech_.el_, this.source);
+      });
+
+      QUnit.test(`<source> two sources through ${appendObj.name}`, function(assert) {
+        const done = assert.async();
+
+        this.source = document.createElement('source');
+        this.source.src = this.sourceOne.src;
+        this.source.type = this.sourceOne.type;
+
+        this.source2 = document.createElement('source');
+        this.source2.src = this.sourceTwo.src;
+        this.source2.type = this.sourceTwo.type;
+
+        this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+        this.player.one('sourceset', (e) => {
+          assert.equal(e.src, this.sourceOne.src, 'source is as expected');
+          done();
+        });
+
+        // since the media el has no source, just appending will
+        // change the source without calling load
+        appendObj.fn(this.player.tech_.el_, this.source);
+
+        // this should not be in the source list or fire a sourceset
+        appendObj.fn(this.player.tech_.el_, this.source2);
+      });
+
+      QUnit.test(`set, remove, load, and set again through ${appendObj.name}`, function(assert) {
+        const done = assert.async();
+
+        this.totalSourcesets = 2;
+        this.source = document.createElement('source');
+        this.source.src = this.sourceTwo.src;
+        this.source.type = this.sourceTwo.type;
+
+        this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+        this.player.one('sourceset', (e1) => {
+          validateSource(assert, this.player, [this.sourceOne]);
+
+          this.player.one('sourceset', (e2) => {
+            validateSource(assert, this.player, [this.sourceTwo], false);
+            done();
+          });
+
+          // reset to no source
+          this.player.tech_.el_.removeAttribute('src');
+          this.player.tech_.el_.load();
+
+          // since the media el has no source, just appending will
+          // change the source without calling load
+          appendObj.fn(this.player.tech_.el_, this.source);
+
+        });
+
+        this.player.tech_.el_.setAttribute('src', this.sourceOne.src);
+      });
+    });
+
+    QUnit.test('no source and load', function(assert) {
+      const done = assert.async();
+
+      this.player = videojs(this.mediaEl, {enableSourceset: true});
+      this.player.tech_.el_.load();
+
+      this.totalSourcesets = 0;
+
+      window.setTimeout(() => {
+        assert.equal(this.sourcesets, 0, 'no sourceset');
+        done();
+      }, wait);
+    });
+  }));
+
   QUnit.module('source change', (subhooks) => testTypes.forEach((testName) => {
     QUnit.module(testName, {
       beforeEach(assert) {
@@ -451,10 +720,10 @@ QUnit[qunitFn]('sourceset', function(hooks) {
       this.mediaEl.removeAttribute('src');
 
       this.player.one('sourceset', (e1) => {
-        assert.equal(e1.src, '', 'we got a sourceset with an empty src');
+        assert.equal(e1.src, this.testSrc.src, 'we got a sourceset with the expected src');
 
         this.player.one('sourceset', (e2) => {
-          assert.equal(e2.src, '', 'we got a sourceset with an empty src');
+          assert.equal(e2.src, this.sourceOne.src, 'we got a sourceset with the expected src');
         });
 
         source.src = this.sourceOne.src;
@@ -474,10 +743,10 @@ QUnit[qunitFn]('sourceset', function(hooks) {
       source.type = this.sourceOne.type;
 
       this.player.one('sourceset', (e1) => {
-        assert.equal(e1.src, '', 'we got a sourceset with an empty src');
+        assert.equal(e1.src, this.sourceOne.src, 'we got a sourceset with the expected src');
 
         this.player.one('sourceset', (e2) => {
-          assert.equal(e2.src, '', 'we got a sourceset with an empty src');
+          assert.equal(e2.src, this.sourceTwo.src, 'we got a sourceset with the expected src');
         });
       });
 
@@ -538,7 +807,7 @@ QUnit[qunitFn]('sourceset', function(hooks) {
       src: 'http://example.com/oceans.flv',
       type: 'video/flv'
     };
-    let sourcesets = 0;
+    const sourcesets = [];
 
     class FakeFlash extends Html5 {
       static isSupported() {
@@ -563,38 +832,27 @@ QUnit[qunitFn]('sourceset', function(hooks) {
       techOrder: ['fakeFlash', 'html5']
     });
 
-    player.src(flashSrc);
-
     player.ready(function() {
-      // the first sourceset comes from our FakeFlash because it extends Html5 tech
-      // which calls load() on dispose for various reasons
-      player.one('sourceset', function(e1) {
-        // ignore the first sourceset that gets called when disposing the original tech
+      // the first sourceset ends up being the second source because when the first source is set
+      // the tech isn't ready so we delay it, then the second source comes and the tech is ready
+      // so it ends up being triggered immediately.
+      player.on('sourceset', (e) => {
+        sourcesets.push(e.src);
 
-        // the second sourceset ends up being the second source because when the first source is set
-        // the tech isn't ready so we delay it, then the second source comes and the tech is ready
-        // so it ends up being triggered immediately.
-        player.one('sourceset', function(e2) {
-          assert.equal(e2.src, sourceTwo.src, 'the second sourceset ends up being the second source');
-          sourcesets++;
+        if (sourcesets.length === 3) {
+          assert.deepEqual([flashSrc.src, sourceTwo.src, sourceOne.src], sourcesets, 'sourceset as expected');
 
-          // now that the tech is ready, we will re-trigger the original sourceset event
-          // and get the first source
-          player.one('sourceset', function(e3) {
-            assert.equal(e3.src, sourceOne.src, 'the third sourceset is the first source');
-            sourcesets++;
-
-            assert.equal(sourcesets, 2, 'two sourcesets');
-            player.dispose();
-            delete Tech.techs_.FakeFlash;
-            done();
-          });
-        });
+          player.dispose();
+          delete Tech.techs_.FakeFlash;
+          done();
+        }
       });
 
       player.src(sourceOne);
       player.src(sourceTwo);
     });
 
+    player.src(flashSrc);
+
   });
 });