parsePlaylist(data) {
    const MEDIA_PLAYLIST_TAGS =
        shaka.hls.ManifestTextParser.MEDIA_PLAYLIST_TAGS;
    const SEGMENT_TAGS = shaka.hls.ManifestTextParser.SEGMENT_TAGS;

    // Get the input as a string.  Normalize newlines to \n.
    let str = shaka.util.StringUtils.fromUTF8(data);
    str = str.replace(/\r\n|\r(?=[^\n]|$)/gm, '\n').trim();

    const lines = str.split(/\n+/m);

    if (!/^#EXTM3U($|[ \t\n])/m.test(lines[0])) {
      throw new shaka.util.Error(
          shaka.util.Error.Severity.CRITICAL,
          shaka.util.Error.Category.MANIFEST,
          shaka.util.Error.Code.HLS_PLAYLIST_HEADER_MISSING);
    }

    /** shaka.hls.PlaylistType */
    let playlistType = shaka.hls.PlaylistType.MASTER;

    // First, look for media playlist tags, so that we know what the playlist
    // type really is before we start parsing.
    // TODO: refactor the for loop for better readability.
    // Whether to skip the next element; initialize to true to skip first elem.
    let skip = true;
    for (const line of lines) {
      // Ignore comments.
      if (shaka.hls.Utils.isComment(line) || skip) {
        skip = false;
        continue;
      }
      const tag = this.parseTag_(line);
      // These tags won't actually be used, so don't increment the global
      // id.
      this.globalId_ -= 1;

      if (MEDIA_PLAYLIST_TAGS.includes(tag.name)) {
        playlistType = shaka.hls.PlaylistType.MEDIA;
        break;
      } else if (tag.name == 'EXT-X-STREAM-INF') {
        skip = true;
      }
    }

    /** {Array<shaka.hls.Tag>} */
    const tags = [];
    // Initialize to "true" to skip the first element.
    skip = true;
    for (let i = 0; i < lines.length; i++) {
      const line = lines[i];
      const next = lines[i + 1];
      // Skip comments
      if (shaka.hls.Utils.isComment(line) || skip) {
        skip = false;
        continue;
      }

      const tag = this.parseTag_(line);
      if (SEGMENT_TAGS.includes(tag.name)) {
        if (playlistType != shaka.hls.PlaylistType.MEDIA) {
          // Only media playlists should contain segment tags
          throw new shaka.util.Error(
              shaka.util.Error.Severity.CRITICAL,
              shaka.util.Error.Category.MANIFEST,
              shaka.util.Error.Code.HLS_INVALID_PLAYLIST_HIERARCHY);
        }

        const segmentsData = lines.splice(i, lines.length - i);
        const segments = this.parseSegments_(segmentsData, tags);
        return new shaka.hls.Playlist(playlistType, tags, segments);
      }

      tags.push(tag);

      // An EXT-X-STREAM-INF tag is followed by a URI of a media playlist.
      // Add the URI to the tag object.
      if (tag.name == 'EXT-X-STREAM-INF') {
        const tagUri = new shaka.hls.Attribute('URI', next);
        tag.addAttribute(tagUri);
        skip = true;
      }
    }

    return new shaka.hls.Playlist(playlistType, tags);
  }
@param {BufferSource} data
@return {!shaka.hls.Playlist}