static serialize(thing) {
    return JSON.stringify(thing, (key, value) => {
      if (typeof value == 'function') {
        // Functions can't be (safely) serialized.
        return undefined;
      }

      if (value instanceof Event || value instanceof shaka.util.FakeEvent) {
        // Events don't serialize to JSON well because of the DOM objects
        // and other complex objects they contain, so we strip these out.
        // Note that using Object.keys or JSON.stringify directly on the event
        // will not capture its properties.  We must use a for loop.
        const simpleEvent = {};
        for (const eventKey in value) {
          const eventValue = value[eventKey];
          if (eventValue && typeof eventValue == 'object') {
            if (eventKey == 'detail') {
              // Keep the detail value, because it contains important
              // information for diagnosing errors.
              simpleEvent[eventKey] = eventValue;
            }
            // Strip out non-null object types because they are complex and we
            // don't need them.
          } else if (eventKey in Event) {
            // Strip out keys that are found on Event itself because they are
            // class-level constants we don't need, like Event.MOUSEMOVE == 16.
          } else {
            simpleEvent[eventKey] = eventValue;
          }
        }
        return simpleEvent;
      }

      if (value instanceof Error) {
        // Errors don't serialize to JSON well, either.  TypeError, for example,
        // turns in "{}", leading to messages like "Error UNKNOWN.UNKNOWN" when
        // deserialized on the sender and displayed in the demo app.
        return shaka.cast.CastUtils.unpackError_(value);
      }

      if (value instanceof TimeRanges) {
        // TimeRanges must be unpacked into plain data for serialization.
        return shaka.cast.CastUtils.unpackTimeRanges_(value);
      }

      if (ArrayBuffer.isView(value) &&
      /** @type {TypedArray} */(value).BYTES_PER_ELEMENT === 1) {
        // Some of our code cares about Uint8Arrays actually being Uint8Arrays,
        // so this gives them special treatment.
        return shaka.cast.CastUtils.unpackUint8Array_(
            /** @type {!Uint8Array} */(value));
      }

      if (typeof value == 'number') {
        // NaN and infinity cannot be represented directly in JSON.
        if (isNaN(value)) {
          return 'NaN';
        }
        if (isFinite(value)) {
          return value;
        }
        if (value < 0) {
          return '-Infinity';
        }
        return 'Infinity';
      }

      return value;
    });
  }
Serialize as JSON, but specially encode things JSON will not otherwise
represent.
@param {?} thing
@return {string}