Skip to content

Commit

Permalink
Add HlsDataSourceStream for convenience
Browse files Browse the repository at this point in the history
HlsDataSource::Read gives quite a bit of control over what segments of
content should be read and to where, which is useful when downloading
parts of raw media, but a good portion of the time HlsDemuxer will be
downloading simple manifest files that measure in the low kilobytes.
The HlsDataSourceStream provides a nice wrapper on a HlsDataSource which
allows either exhaustive reading of an entire URI, or slowly progressing
through the URI's data stream, but only forwards.

Bug: 1266991
Change-Id: Id8f459e25e47108efe688e43cf4418c32f56534d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4214923
Reviewed-by: Thomas Guilbert <tguilbert@chromium.org>
Commit-Queue: Ted (Chromium) Meyer <tmathmeyer@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1103648}
  • Loading branch information
tm-chromium authored and Chromium LUCI CQ committed Feb 10, 2023
1 parent ccb7a98 commit c0836fd
Show file tree
Hide file tree
Showing 4 changed files with 384 additions and 1 deletion.
5 changes: 4 additions & 1 deletion media/filters/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,10 @@ source_set("unit_tests") {
}

if (enable_hls_demuxer) {
sources += [ "hls_demuxer_unittest.cc" ]
sources += [
"hls_data_source_provider_unittest.cc",
"hls_demuxer_unittest.cc",
]
}

if (media_use_ffmpeg) {
Expand Down
73 changes: 73 additions & 0 deletions media/filters/hls_data_source_provider.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,77 @@ HlsDataSource::~HlsDataSource() = default;

HlsDataSourceProvider::~HlsDataSourceProvider() = default;

HlsDataSourceStream::HlsDataSourceStream(HlsDataSourceStream&&) = default;
HlsDataSourceStream::~HlsDataSourceStream() = default;
HlsDataSourceStream::HlsDataSourceStream(
std::unique_ptr<HlsDataSource> data_source)
: data_source_(std::move(data_source)) {}

bool HlsDataSourceStream::CanReadMore() const {
auto ds_size = data_source_->GetSize();
if (ds_size.has_value()) {
return *ds_size > total_bytes_read_;
}
// If there's no data size on the source, then assume we can keep reading.
return true;
}

size_t HlsDataSourceStream::BytesInBuffer() const {
return buffer_.size();
}

base::StringPiece HlsDataSourceStream::AsStringPiece() const {
return base::StringPiece(reinterpret_cast<const char*>(buffer_.data()),
buffer_.size());
}

const uint8_t* HlsDataSourceStream::AsRawData() const {
return buffer_.data();
}

void HlsDataSourceStream::Flush() {
buffer_.resize(0);
}

void HlsDataSourceStream::ReadAll(ReadCb read_cb) && {
std::move(*this).ReadChunk(base::BindOnce(
[](ReadCb cb, ReadResult m_stream) {
if (!m_stream.has_value()) {
std::move(cb).Run(std::move(m_stream).error().AddHere());
return;
}
auto stream = std::move(m_stream).value();
if (stream.data_source_->GetSize().has_value() &&
stream.CanReadMore()) {
std::move(stream).ReadAll(std::move(cb));
return;
}
std::move(cb).Run(std::move(stream));
},
std::move(read_cb)));
}

void HlsDataSourceStream::ReadChunk(ReadCb cb, size_t read_size) && {
size_t original_buffer_size = BytesInBuffer();
buffer_.insert(buffer_.end(), read_size, 0);
uint8_t* destination = buffer_.data() + original_buffer_size;

data_source_->Read(
total_bytes_read_, read_size, destination,
base::BindOnce(
[](ReadCb cb, size_t original_size,
HlsDataSourceStream captured_stream,
HlsDataSource::ReadStatus::Or<size_t> result) {
if (!result.has_value()) {
std::move(cb).Run(std::move(result).error());
return;
}
size_t bytes_read = std::move(result).value();
captured_stream.buffer_.resize(original_size + bytes_read);
captured_stream.total_bytes_read_ += bytes_read;
std::move(cb).Run(std::move(captured_stream));
},
std::move(cb), original_buffer_size, std::move(*this)));
}

} // namespace media
53 changes: 53 additions & 0 deletions media/filters/hls_data_source_provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@

namespace media {

namespace {

// A small-ish size that it should probably be able to get most manifests in
// a single chunk. Chosen somewhat arbitrarily otherwise.
constexpr size_t kDefaultReadSize = 0xFFFF;

} // namespace

class HlsDemuxer;

// Interface which can provide HlsDemuxer with data, respecting byterange
Expand Down Expand Up @@ -83,6 +91,51 @@ class MEDIA_EXPORT HlsDataSourceProvider {
RequestCb) = 0;
};

// 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:
using Self = HlsDataSourceStream;
using ReadResult = HlsDataSource::ReadStatus::Or<Self>;

// Callback fired when attempting to read the entire datasource at once.
using ReadCb = base::OnceCallback<void(ReadResult)>;

HlsDataSourceStream(std::unique_ptr<HlsDataSource> data_source);
~HlsDataSourceStream();
HlsDataSourceStream(const HlsDataSourceStream&) = delete;
HlsDataSourceStream(HlsDataSourceStream&&);

// Helpers for checking the internal state of the stream.
bool CanReadMore() const;
size_t BytesInBuffer() const;

// Helpers for accessing the buffer.
base::StringPiece AsStringPiece() const;
const uint8_t* AsRawData() const;

// Reset the internal buffer.
void Flush();

// Read the entire data source at once, unless the data source has an
// undetermined size. In the case of undetermined size, ReadAll's behavior
// will default to chunk-by-chunk reading.
void ReadAll(ReadCb cb) &&;

// Read just one chunk of data of a given size.
void ReadChunk(ReadCb cb, size_t read_size = kDefaultReadSize) &&;

private:
// The data source to read from.
std::unique_ptr<HlsDataSource> data_source_;

// the buffer of data to read into.
std::vector<uint8_t> buffer_;

// The total number of bytes read. Not affected by |Flush|.
size_t total_bytes_read_ = 0;
};

} // namespace media

#endif // MEDIA_FILTERS_HLS_DATA_SOURCE_PROVIDER_H_

0 comments on commit c0836fd

Please sign in to comment.