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
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203

media / filters / hls_data_source_provider.h [blame]

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_FILTERS_HLS_DATA_SOURCE_PROVIDER_H_
#define MEDIA_FILTERS_HLS_DATA_SOURCE_PROVIDER_H_

#include <cstdint>
#include <memory>
#include <optional>
#include <string_view>

#include "base/containers/queue.h"
#include "base/functional/callback.h"
#include "base/types/id_type.h"
#include "media/base/media_export.h"
#include "media/base/status.h"
#include "media/formats/hls/types.h"
#include "url/gurl.h"

namespace media {

class HlsDataSourceStream;

// Interface which can provide data sources, given a URI and an optional
// byterange. This interface should be used via `base::SequenceBound` to proxy
// requests across the media thread and the main thread.
class MEDIA_EXPORT HlsDataSourceProvider {
 public:
  virtual ~HlsDataSourceProvider() = 0;

  struct ReadStatusTraits {
    enum class Codes : StatusCodeType {
      kError,
      kStopped,
      kAborted,
    };
    static constexpr StatusGroupType Group() {
      return "HlsDataSourceProvider::ReadStatus";
    }
  };

  using ReadStatus = TypedStatus<ReadStatusTraits>;
  using ReadResult = ReadStatus::Or<std::unique_ptr<HlsDataSourceStream>>;
  using ReadCb = base::OnceCallback<void(ReadResult)>;

  // Represents reading from a specific URI at the given byte range. Multiple
  // segments can be added to a read queue to join chunks together from either
  // multiple URIs or from multiple disjoing ranges on the same URI.
  struct UrlDataSegment {
    const GURL uri;
    const std::optional<hls::types::ByteRange> range;
    const bool bypass_cache;
  };
  using SegmentQueue = base::queue<UrlDataSegment>;

  // Kicks off a read to a chain of segments, and replies with a stream
  // reference which can be used to continue fetching partial data.
  virtual void ReadFromCombinedUrlQueue(SegmentQueue segments,
                                        ReadCb callback) = 0;

  // Continues to read from an existing stream.
  virtual void ReadFromExistingStream(
      std::unique_ptr<HlsDataSourceStream> stream,
      ReadCb callback) = 0;

  // Aborts all pending reads and calls `callback` when finished.
  virtual void AbortPendingReads(base::OnceClosure callback) = 0;

  // Helper function for reading from a single segment by creating a queue of
  // size 1 for use with `ReadFromCombinedUrlQueue`
  void ReadFromUrl(UrlDataSegment segment, ReadCb callback);
};

// A buffer-owning wrapper for an HlsDataSource which can be instructed to
// read an entire data source, or to retrieve it in chunks.
class MEDIA_EXPORT HlsDataSourceStream {
 public:
  // The response to a stream read includes a raw pointer back to the stream
  // which allows accessing the data from a read as well as caching a partially
  // read stream handle for continued downloading.
  using StreamId = base::IdType32<HlsDataSourceStream>;

  // Create a stream where `on_destructed_cb` is used to give notice that this
  // class is being destroyed. This class isn't safe to access from anything
  // except for an ownership-holding smart pointer, as the destruction cb may
  // do work across threads.
  HlsDataSourceStream(StreamId stream_id,
                      HlsDataSourceProvider::SegmentQueue segments,
                      base::OnceClosure on_destructed_cb);
  ~HlsDataSourceStream();

  // Streams use an ID associated with a MultiBufferDataSource without
  // owning it.
  StreamId stream_id() const { return stream_id_; }

  // This is the byte position in the MultiBufferDataSource where new data
  // will be read from. This only ever goes up, because these streams are not
  // rewindable.
  size_t read_position() const { return read_position_; }

  size_t buffer_size() const { return buffer_.size(); }

  std::optional<size_t> max_read_position() const { return max_read_position_; }

  const uint8_t* raw_data() const { return buffer_.data(); }

  uint64_t memory_usage() const { return memory_usage_; }

  bool would_taint_origin() const { return would_taint_origin_; }

  // Allows the stream creator to update memory usage after the first or after
  // subsequent reads.
  void set_total_memory_usage(uint64_t usage) { memory_usage_ = usage; }

  // A stream's origin is considered tainted if any backing data source involved
  // in this playback is tainted.
  void set_would_taint_origin() { would_taint_origin_ = true; }

  // Often the network data for HLS consists of plain-text manifest files, so
  // this supports accessing the fetched data as a string view.
  std::string_view AsString() const;

  // Determines whether the current segment has finished reading, and there are
  // more segments in the queue to read from.
  bool RequiresNextDataSource() const;

  // Gets the next segment URI from the queue of segments. It is invalid to call
  // this method if `RequiresNextDataSource` does not return true. This
  // method will also update the internal range if the segment has one.
  GURL GetNextSegmentURI();

  // Gets the next segment URI and its cache bypass option from the queue of
  // segments. It is invalid to call this method if `RequiresNextDataSource`
  // does not return true. This method will also update the internal range if
  // the segment has one.
  std::pair<GURL, bool> GetNextSegmentURIAndCacheStatus();

  // Has the stream read all possible data?
  bool CanReadMore() const;

  // Clears the internal buffer of data. Continual reads will refill the buffer
  // and reading without clearing will append to the end of the buffer.
  void Clear();

  // Used by a HlsDataSourceProvider implementation to finish adding data to
  // the internal buffer.
  void UnlockStreamPostWrite(int read_size, bool end_of_stream);

  // Used by a HlsDataSourceProvider implementation to start adding new data,
  // which means ensuring that there is enough space for the expected write, as
  // well as returning the correct buffer address to write into.
  uint8_t* LockStreamForWriting(int ensure_minimum_space);

 private:
  const StreamId stream_id_;

  // Active buffer data. Reading without clearing will append new data
  // to the end of the buffer. Clearing will not reset the read-head, but will
  // empty this buffer.
  // TODO(crbug.com/40057824): Consider swapping out the vector with a more
  // size-flexible data structure to avoid resizing.
  std::vector<uint8_t> buffer_;

  // This is critical to security. Once set to true, it must _never_ be set back
  // to false.
  bool would_taint_origin_ = false;

  // The memory usage represents the total memory usage for _all_ streams used
  // in this playback.
  uint64_t memory_usage_ = 0;

  size_t read_position_ = 0;

  // The write index into `buffer_`. This gets reset on flush.
  size_t write_index_ = 0;

  // If this optional value is set, then data can't be read past this maximum
  // value.
  std::optional<size_t> max_read_position_;

  // The data source read response indicated that the stream has ended.
  bool reached_end_of_stream_ = false;

  // The stream is unable to start a second write or clear until it is unlocked
  // by UnlockStreamPostWrite.
  bool stream_locked_ = false;

  // The queue of segments to read from.
  HlsDataSourceProvider::SegmentQueue segments_;

  // Does this stream require a reset to get the next data source.
  bool requires_next_data_source_;

  base::OnceClosure on_destructed_cb_;

  SEQUENCE_CHECKER(sequence_checker_);
  base::WeakPtrFactory<HlsDataSourceStream> weak_factory_{this};
};

}  // namespace media

#endif  // MEDIA_FILTERS_HLS_DATA_SOURCE_PROVIDER_H_