summaryrefslogtreecommitdiff
path: root/chromium/media/formats/hls/playlist.cc
blob: eb652e6b835468f30808a22df95d49ecc03465d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/formats/hls/playlist.h"

#include "media/formats/hls/items.h"
#include "media/formats/hls/playlist_common.h"
#include "media/formats/hls/tags.h"
#include "media/formats/hls/types.h"
#include "url/gurl.h"

namespace media::hls {

Playlist::Playlist(GURL uri,
                   types::DecimalInteger version,
                   bool independent_segments)
    : uri_(std::move(uri)),
      version_(version),
      independent_segments_(independent_segments) {}

Playlist::Playlist(Playlist&&) = default;
Playlist& Playlist::operator=(Playlist&&) = default;
Playlist::~Playlist() = default;

// static
ParseStatus::Or<Playlist::Identification> Playlist::IdentifyPlaylist(
    base::StringPiece source) {
  absl::optional<Kind> playlist_kind;
  absl::optional<XVersionTag> version_tag;

  // Iterate through playlist lines until we can identify the version and the
  // playlist kind.
  SourceLineIterator iter(source);
  while (!playlist_kind.has_value() || !version_tag.has_value()) {
    auto item_result = GetNextLineItem(&iter);
    if (item_result.has_error()) {
      auto error = std::move(item_result).error();

      // Only tolerated error is EOF
      if (error.code() == ParseStatusCode::kReachedEOF) {
        break;
      }

      return error;
    }

    auto item = std::move(item_result).value();
    if (auto* tag = absl::get_if<TagItem>(&item)) {
      // We can't make any assumptions on unknown tags
      if (!tag->GetName().has_value()) {
        continue;
      }
      const auto tag_name = tag->GetName().value();

      // If this is a version tag, try parsing it to determine the version
      if (tag_name == ToTagName(CommonTagName::kXVersion)) {
        auto error = ParseUniqueTag(*tag, version_tag);
        if (error.has_value()) {
          return std::move(error).value();
        }

        // Ensure that the version is supported by this implementation
        if (version_tag->version < Playlist::kMinSupportedVersion ||
            version_tag->version > Playlist::kMaxSupportedVersion) {
          return ParseStatusCode::kPlaylistHasUnsupportedVersion;
        }

        continue;
      }

      // Otherwise use the tag's kind to identify playlist kind without
      // attempting to parse it.
      switch (GetTagKind(tag_name)) {
        case TagKind::kCommonTag:
          break;
        case TagKind::kMultivariantPlaylistTag:
          if (playlist_kind == Kind::kMediaPlaylist) {
            return ParseStatusCode::kMediaPlaylistHasMultivariantPlaylistTag;
          }
          playlist_kind = Kind::kMultivariantPlaylist;
          break;
        case TagKind::kMediaPlaylistTag:
          if (playlist_kind == Kind::kMultivariantPlaylist) {
            return ParseStatusCode::kMultivariantPlaylistHasMediaPlaylistTag;
          }
          playlist_kind = Kind::kMediaPlaylist;
          break;
      }
    }
  }

  return Identification{
      // If the playlist did not contain a version tag, version is implicitly
      // `kDefaultVersion`.
      .version = version_tag ? version_tag->version : kDefaultVersion,

      // Media playlists must contain the EXT-X-TARGETDURATION tag, so if we
      // never encountered a media playlist tag it must be an (empty)
      // multivariant playlist.
      .kind =
          playlist_kind ? playlist_kind.value() : Kind::kMultivariantPlaylist,
  };
}

}  // namespace media::hls