blob: 1ab66dad2daf7873e80a7aa312bfdcb2788783f0 [file] [log] [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.
#include "media/formats/hls/segment_stream.h"
namespace media::hls {
SegmentStream::SegmentIndex::SegmentIndex(const MediaSegment& segment)
: SegmentIndex(segment.GetMediaSequenceNumber(),
segment.GetDiscontinuitySequenceNumber()) {}
SegmentStream::SegmentIndex::SegmentIndex(types::DecimalInteger discontinuity,
types::DecimalInteger media)
: media_sequence_(media), discontinuity_sequence_(discontinuity) {}
bool SegmentStream::SegmentIndex::operator<(
const SegmentStream::SegmentIndex& other) const {
return (discontinuity_sequence_ < other.discontinuity_sequence_) ||
(discontinuity_sequence_ == other.discontinuity_sequence_ &&
media_sequence_ < other.media_sequence_);
}
bool SegmentStream::SegmentIndex::operator<=(
const SegmentStream::SegmentIndex& other) const {
return *this < other || *this == other;
}
bool SegmentStream::SegmentIndex::operator==(
const SegmentStream::SegmentIndex& other) const {
return discontinuity_sequence_ == other.discontinuity_sequence_ &&
media_sequence_ == other.media_sequence_;
}
bool SegmentStream::SegmentIndex::operator>(
const SegmentStream::SegmentIndex& other) const {
return !(*this == other) && !(*this < other);
}
SegmentStream::SegmentIndex SegmentStream::SegmentIndex::MaxOf(
const MediaSegment& other) const {
SegmentIndex other_index(other);
if (other_index < *this) {
return *this;
}
return other_index;
}
SegmentStream::SegmentIndex SegmentStream::SegmentIndex::Next() const {
return {discontinuity_sequence_, media_sequence_ + 1};
}
SegmentStream::~SegmentStream() = default;
SegmentStream::SegmentStream(scoped_refptr<MediaPlaylist> playlist,
bool seekable)
: seekable_(seekable),
next_segment_start_(base::Seconds(0)),
active_playlist_(std::move(playlist)) {
for (const auto& segment : active_playlist_->GetSegments()) {
segments_.push(segment);
highest_segment_index_ = highest_segment_index_.MaxOf(*segment);
}
}
SegmentInfo SegmentStream::GetNextSegment() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!segments_.empty());
auto segment = std::move(segments_.front());
segments_.pop();
base::TimeDelta previous_segment_start = next_segment_start_;
next_segment_start_ += segment->GetDuration();
return std::make_tuple(segment, previous_segment_start, next_segment_start_);
}
bool SegmentStream::Seek(base::TimeDelta seek_time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!seekable_) {
return false;
}
segments_ = {};
base::TimeDelta start_time = base::Seconds(0);
for (const auto& segment : active_playlist_->GetSegments()) {
base::TimeDelta end_time = start_time + segment->GetDuration();
if (seek_time < end_time) {
segments_.push(segment);
}
if (segments_.size() == 1) {
// Set the end time for the virtually-popped-sequence to be the current
// start time.
next_segment_start_ = start_time;
}
start_time = end_time;
}
return !segments_.empty();
}
void SegmentStream::SetNewPlaylist(scoped_refptr<MediaPlaylist> playlist) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
active_playlist_ = std::move(playlist);
const auto& segments = active_playlist_->GetSegments();
if (segments.empty()) {
// No new segments.
// TODO(crbug.com/40057824): Should this be an error? I do not know if this
// ever happens in the wild. I can imagine that it does, hence not raising
// an error here for now. The spec doesn't seem to clear it up.
return;
}
bool must_keep_encrypted_ = false;
if (!Exhausted()) {
// If the head is a non-fresh encrypted segment, keep it.
must_keep_encrypted_ = !!segments_.front()->GetEncryptionData() &&
!segments_.front()->HasNewEncryptionData();
}
SegmentIndex starting_segment_index = {0, 0};
if (Exhausted()) {
if (seekable_) {
// If a VOD stream is exhausted, there is nothing to append. Seeking later
// will use the new active playlist's queue.
return;
}
starting_segment_index = highest_segment_index_.Next();
} else {
starting_segment_index = SegmentIndex(*segments_.front());
}
if (must_keep_encrypted_) {
starting_segment_index = starting_segment_index.Next();
}
base::queue<scoped_refptr<MediaSegment>> new_queue;
if (must_keep_encrypted_) {
new_queue.push(std::move(segments_.front()));
}
for (const auto& segment : segments) {
auto segment_sequence_index = SegmentIndex(*segment);
if (starting_segment_index <= segment_sequence_index) {
new_queue.push(segment);
}
if (segment_sequence_index > highest_segment_index_) {
highest_segment_index_ = segment_sequence_index;
}
}
segments_ = std::move(new_queue);
}
base::TimeDelta SegmentStream::GetMaxDuration() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return active_playlist_->GetTargetDuration();
}
bool SegmentStream::Exhausted() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return segments_.empty();
}
base::TimeDelta SegmentStream::NextSegmentStartTime() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return next_segment_start_;
}
bool SegmentStream::PlaylistHasSegments() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return !active_playlist_->GetSegments().empty();
}
size_t SegmentStream::QueueSize() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return segments_.size();
}
void SegmentStream::ResetExpectingFutureManifest(base::TimeDelta time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
segments_ = {};
next_segment_start_ = time;
}
} // namespace media::hls