Remove Time Scale Assumptions From Text Chunker
Before, the text chunker would assume that all text streams were in MS, which is not a safe assumption to make. This changes it to take the time scale from StreamInfo and work natively with scaled time units. This required updating the tests. While doing so the tests were rewritten with the goal to make them easier to read. Closes: #399 Change-Id: Ib792ad306f40d749763418cde645337913a6046b
This commit is contained in:
parent
30b189cb99
commit
dbce84f3ea
|
@ -12,27 +12,10 @@ namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
namespace {
|
namespace {
|
||||||
const size_t kStreamIndex = 0;
|
const size_t kStreamIndex = 0;
|
||||||
|
|
||||||
std::shared_ptr<const SegmentInfo> MakeSegmentInfo(int64_t start_ms,
|
|
||||||
int64_t end_ms) {
|
|
||||||
DCHECK_LT(start_ms, end_ms);
|
|
||||||
|
|
||||||
std::shared_ptr<SegmentInfo> info = std::make_shared<SegmentInfo>();
|
|
||||||
info->start_timestamp = start_ms;
|
|
||||||
info->duration = end_ms - start_ms;
|
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TextChunker::TextChunker(int64_t segment_duration_ms)
|
TextChunker::TextChunker(double segment_duration_in_seconds)
|
||||||
: segment_duration_ms_(segment_duration_ms),
|
: segment_duration_in_seconds_(segment_duration_in_seconds){};
|
||||||
segment_start_ms_(0),
|
|
||||||
segment_expected_end_ms_(segment_duration_ms) {}
|
|
||||||
|
|
||||||
Status TextChunker::InitializeInternal() {
|
|
||||||
return Status::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status TextChunker::Process(std::unique_ptr<StreamData> data) {
|
Status TextChunker::Process(std::unique_ptr<StreamData> data) {
|
||||||
switch (data->stream_data_type) {
|
switch (data->stream_data_type) {
|
||||||
|
@ -49,75 +32,89 @@ Status TextChunker::Process(std::unique_ptr<StreamData> data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Status TextChunker::OnFlushRequest(size_t input_stream_index) {
|
Status TextChunker::OnFlushRequest(size_t input_stream_index) {
|
||||||
// Keep outputting segments until all the samples leave the system.
|
// Keep outputting segments until all the samples leave the system. Calling
|
||||||
while (segment_samples_.size()) {
|
// |DispatchSegment| will remove samples over time.
|
||||||
RETURN_IF_ERROR(EndSegment(segment_expected_end_ms_));
|
while (samples_in_current_segment_.size()) {
|
||||||
|
RETURN_IF_ERROR(DispatchSegment(segment_duration_));
|
||||||
}
|
}
|
||||||
|
|
||||||
return FlushAllDownstreams();
|
return FlushAllDownstreams();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status TextChunker::OnStreamInfo(std::shared_ptr<const StreamInfo> info) {
|
Status TextChunker::OnStreamInfo(std::shared_ptr<const StreamInfo> info) {
|
||||||
// There is no information we need from the stream info, so just pass it
|
time_scale_ = info->time_scale();
|
||||||
// downstream.
|
segment_duration_ = ScaleTime(segment_duration_in_seconds_);
|
||||||
|
|
||||||
return DispatchStreamInfo(kStreamIndex, std::move(info));
|
return DispatchStreamInfo(kStreamIndex, std::move(info));
|
||||||
}
|
}
|
||||||
|
|
||||||
Status TextChunker::OnCueEvent(std::shared_ptr<const CueEvent> event) {
|
Status TextChunker::OnCueEvent(std::shared_ptr<const CueEvent> event) {
|
||||||
// We are going to cut the current segment into two using the event's time as
|
// We are going to end the current segment prematurely using the cue event's
|
||||||
// the division.
|
// time as the new segment end.
|
||||||
const int64_t cue_time_in_ms = event->time_in_seconds * 1000;
|
|
||||||
|
|
||||||
// In the case that there is a gap with no samples between the last sample
|
// Because the cue should have been inserted into the stream such that no
|
||||||
// and the cue event, output all the segments until we get to the segment that
|
// later sample could start before it does, we know that there should
|
||||||
// the cue event interrupts.
|
// be no later samples starting before the cue event.
|
||||||
while (segment_expected_end_ms_ < cue_time_in_ms) {
|
|
||||||
RETURN_IF_ERROR(EndSegment(segment_expected_end_ms_));
|
// Convert the event's time to be scaled to the time of each sample.
|
||||||
|
const int64_t event_time = ScaleTime(event->time_in_seconds);
|
||||||
|
|
||||||
|
// Output all full segments before the segment that the cue event interupts.
|
||||||
|
while (segment_start_ + segment_duration_ < event_time) {
|
||||||
|
RETURN_IF_ERROR(DispatchSegment(segment_duration_));
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_IF_ERROR(EndSegment(cue_time_in_ms));
|
const int64_t shorten_duration = event_time - segment_start_;
|
||||||
RETURN_IF_ERROR(DispatchCueEvent(kStreamIndex, std::move(event)));
|
|
||||||
|
|
||||||
return Status::OK;
|
RETURN_IF_ERROR(DispatchSegment(shorten_duration));
|
||||||
|
return DispatchCueEvent(kStreamIndex, std::move(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
Status TextChunker::OnTextSample(std::shared_ptr<const TextSample> sample) {
|
Status TextChunker::OnTextSample(std::shared_ptr<const TextSample> sample) {
|
||||||
// Output all segments that come before our new sample.
|
// Output all segments that come before our new sample.
|
||||||
while (segment_expected_end_ms_ <= sample->start_time()) {
|
const int64_t sample_start = sample->start_time();
|
||||||
RETURN_IF_ERROR(EndSegment(segment_expected_end_ms_));
|
while (sample_start >= segment_start_ + segment_duration_) {
|
||||||
|
RETURN_IF_ERROR(DispatchSegment(segment_duration_));
|
||||||
}
|
}
|
||||||
|
|
||||||
segment_samples_.push_back(std::move(sample));
|
samples_in_current_segment_.push_back(std::move(sample));
|
||||||
|
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status TextChunker::EndSegment(int64_t segment_actual_end_ms) {
|
Status TextChunker::DispatchSegment(int64_t duration) {
|
||||||
|
DCHECK_GT(duration, 0) << "Segment duration should always be positive";
|
||||||
|
|
||||||
// Output all the samples that are part of the segment.
|
// Output all the samples that are part of the segment.
|
||||||
for (const auto& sample : segment_samples_) {
|
for (const auto& sample : samples_in_current_segment_) {
|
||||||
RETURN_IF_ERROR(DispatchTextSample(kStreamIndex, sample));
|
RETURN_IF_ERROR(DispatchTextSample(kStreamIndex, sample));
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_IF_ERROR(DispatchSegmentInfo(
|
// Output the segment info.
|
||||||
kStreamIndex, MakeSegmentInfo(segment_start_ms_, segment_actual_end_ms)));
|
std::shared_ptr<SegmentInfo> info = std::make_shared<SegmentInfo>();
|
||||||
|
info->start_timestamp = segment_start_;
|
||||||
|
info->duration = duration;
|
||||||
|
RETURN_IF_ERROR(DispatchSegmentInfo(kStreamIndex, std::move(info)));
|
||||||
|
|
||||||
// Create a new segment that comes right after the old segment and remove all
|
// Move onto the next segment.
|
||||||
// samples that don't cross over into the new segment.
|
const int64_t new_segment_start = segment_start_ + duration;
|
||||||
StartNewSegment(segment_actual_end_ms);
|
segment_start_ = new_segment_start;
|
||||||
|
|
||||||
|
// Remove all samples that end before the (new) current segment started.
|
||||||
|
samples_in_current_segment_.remove_if(
|
||||||
|
[new_segment_start](const std::shared_ptr<const TextSample>& sample) {
|
||||||
|
// For the sample to even be in this list, it should have started
|
||||||
|
// before the (new) current segment.
|
||||||
|
DCHECK_LT(sample->start_time(), new_segment_start);
|
||||||
|
return sample->EndTime() <= new_segment_start;
|
||||||
|
});
|
||||||
|
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextChunker::StartNewSegment(int64_t start_ms) {
|
int64_t TextChunker::ScaleTime(double seconds) const {
|
||||||
segment_start_ms_ = start_ms;
|
DCHECK_GT(time_scale_, 0) << "Need positive time scale to scale time.";
|
||||||
segment_expected_end_ms_ = start_ms + segment_duration_ms_;
|
return static_cast<int64_t>(seconds * time_scale_);
|
||||||
|
|
||||||
// Remove all samples that no longer overlap with the new segment.
|
|
||||||
segment_samples_.remove_if(
|
|
||||||
[start_ms](const std::shared_ptr<const TextSample>& sample) {
|
|
||||||
return sample->EndTime() <= start_ms;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -14,15 +14,19 @@
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
|
// Media handler for taking a single stream of text samples and inserting
|
||||||
|
// segment info based on a fixed segment duration and on cue events. The
|
||||||
|
// only time a segment's duration will not match the fixed segment duration
|
||||||
|
// is when a cue event is seen.
|
||||||
class TextChunker : public MediaHandler {
|
class TextChunker : public MediaHandler {
|
||||||
public:
|
public:
|
||||||
explicit TextChunker(int64_t segment_duration_ms);
|
explicit TextChunker(double segment_duration_in_seconds);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TextChunker(const TextChunker&) = delete;
|
TextChunker(const TextChunker&) = delete;
|
||||||
TextChunker& operator=(const TextChunker&) = delete;
|
TextChunker& operator=(const TextChunker&) = delete;
|
||||||
|
|
||||||
Status InitializeInternal() override;
|
Status InitializeInternal() override { return Status::OK; }
|
||||||
|
|
||||||
Status Process(std::unique_ptr<StreamData> stream_data) override;
|
Status Process(std::unique_ptr<StreamData> stream_data) override;
|
||||||
Status OnFlushRequest(size_t input_stream_index) override;
|
Status OnFlushRequest(size_t input_stream_index) override;
|
||||||
|
@ -31,17 +35,27 @@ class TextChunker : public MediaHandler {
|
||||||
Status OnCueEvent(std::shared_ptr<const CueEvent> cue);
|
Status OnCueEvent(std::shared_ptr<const CueEvent> cue);
|
||||||
Status OnTextSample(std::shared_ptr<const TextSample> sample);
|
Status OnTextSample(std::shared_ptr<const TextSample> sample);
|
||||||
|
|
||||||
Status EndSegment(int64_t segment_actual_end_ms);
|
// This does two things that should always happen together:
|
||||||
void StartNewSegment(int64_t start_ms);
|
// 1. Dispatch all the samples and a segment info for the time range
|
||||||
|
// segment_start_ to segment_start_ + duration
|
||||||
|
// 2. Set the next segment to start at segment_start_ + duration and
|
||||||
|
// remove all samples that don't last into that segment.
|
||||||
|
Status DispatchSegment(int64_t duration);
|
||||||
|
|
||||||
int64_t segment_duration_ms_;
|
int64_t ScaleTime(double seconds) const;
|
||||||
|
|
||||||
// The segment that we are currently outputting samples for. The segment
|
double segment_duration_in_seconds_;
|
||||||
// will end once a new sample with start time greater or equal to the
|
|
||||||
// segment's end time arrives.
|
int64_t time_scale_ = -1; // Set in OnStreamInfo
|
||||||
int64_t segment_start_ms_;
|
|
||||||
int64_t segment_expected_end_ms_;
|
// Time values are in scaled units.
|
||||||
std::list<std::shared_ptr<const TextSample>> segment_samples_;
|
int64_t segment_start_ = 0;
|
||||||
|
int64_t segment_duration_ = -1; // Set in OnStreamInfo
|
||||||
|
|
||||||
|
// All samples that make up the current segment. We must store the samples
|
||||||
|
// until the segment ends because a cue event may end the segment sooner
|
||||||
|
// than we expected.
|
||||||
|
std::list<std::shared_ptr<const TextSample>> samples_in_current_segment_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
|
@ -27,7 +27,7 @@ const size_t kOutput = 0;
|
||||||
const bool kEncrypted = true;
|
const bool kEncrypted = true;
|
||||||
const bool kSubSegment = true;
|
const bool kSubSegment = true;
|
||||||
|
|
||||||
const int64_t kTick = 50;
|
const uint64_t kTimescaleMs = 1000;
|
||||||
|
|
||||||
const char* kNoId = "";
|
const char* kNoId = "";
|
||||||
const char* kNoSettings = "";
|
const char* kNoSettings = "";
|
||||||
|
@ -36,25 +36,33 @@ const char* kNoPayload = "";
|
||||||
|
|
||||||
class TextChunkerTest : public MediaHandlerTestBase {
|
class TextChunkerTest : public MediaHandlerTestBase {
|
||||||
protected:
|
protected:
|
||||||
void Init(int64_t segment_duration) {
|
void Init(double segment_duration) {
|
||||||
ASSERT_OK(SetUpAndInitializeGraph(
|
ASSERT_OK(SetUpAndInitializeGraph(
|
||||||
std::make_shared<TextChunker>(segment_duration), kInputs, kOutputs));
|
std::make_shared<TextChunker>(segment_duration), kInputs, kOutputs));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// S0 S1
|
// Verify that when a sample elapses a full segment, that it only appears
|
||||||
// | |
|
// in the one segment.
|
||||||
// |[---A---]|
|
//
|
||||||
// | |
|
// Segment Duration = 100 MS
|
||||||
|
//
|
||||||
|
// TIME (ms):0 5 1
|
||||||
|
// 0 0
|
||||||
|
// 0
|
||||||
|
// SAMPLES :[-----A-----]
|
||||||
|
// SEGMENTS : ^
|
||||||
|
//
|
||||||
TEST_F(TextChunkerTest, SampleEndingOnSegmentStart) {
|
TEST_F(TextChunkerTest, SampleEndingOnSegmentStart) {
|
||||||
const int64_t kSegmentStart = 0;
|
const double kSegmentDurationSec = 0.1;
|
||||||
const int64_t kSegmentDuration = kTick;
|
const int64_t kSegmentDurationMs = 100;
|
||||||
|
|
||||||
const int64_t kSampleStart = 0;
|
const int64_t kSegment0Start = 0;
|
||||||
const int64_t kSampleDuration = kTick;
|
|
||||||
const int64_t kSampleEnd = kSampleStart + kSampleDuration;
|
|
||||||
|
|
||||||
Init(kSegmentDuration);
|
const int64_t kSampleAStart = 0;
|
||||||
|
const int64_t kSampleAEnd = 100;
|
||||||
|
|
||||||
|
Init(kSegmentDurationSec);
|
||||||
|
|
||||||
{
|
{
|
||||||
testing::InSequence s;
|
testing::InSequence s;
|
||||||
|
@ -62,12 +70,12 @@ TEST_F(TextChunkerTest, SampleEndingOnSegmentStart) {
|
||||||
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
|
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
|
||||||
|
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSampleStart, kSampleEnd,
|
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegmentStart, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,29 +83,36 @@ TEST_F(TextChunkerTest, SampleEndingOnSegmentStart) {
|
||||||
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
|
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
kStreamIndex,
|
kStreamIndex,
|
||||||
GetTextSample(kNoId, kSampleStart, kSampleEnd, kNoPayload))));
|
GetTextSample(kNoId, kSampleAStart, kSampleAEnd, kNoPayload))));
|
||||||
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
||||||
}
|
}
|
||||||
|
|
||||||
// S0 S1 S2
|
// Verify that samples only appear in the correct segments when they only exist
|
||||||
// | | |
|
// in one segment.
|
||||||
// |[-A-] | |
|
//
|
||||||
// | |[-B-] |
|
// Segment Duration = 100 MS
|
||||||
// | | |
|
//
|
||||||
|
// TIME (ms):0 5 1 1 2
|
||||||
|
// 0 0 5 0
|
||||||
|
// 0 0 0
|
||||||
|
// SAMPLES : [--A--]
|
||||||
|
// [--B--]
|
||||||
|
// SEGMENTS : ^ ^
|
||||||
|
//
|
||||||
TEST_F(TextChunkerTest, CreatesSegmentsForSamples) {
|
TEST_F(TextChunkerTest, CreatesSegmentsForSamples) {
|
||||||
const int64_t kSegmentDuration = 2 * kTick;
|
const double kSegmentDurationSec = 0.1;
|
||||||
|
const int64_t kSegmentDurationMs = 100;
|
||||||
|
|
||||||
const int64_t kSegment0Start = 0;
|
const int64_t kSegment0Start = 0;
|
||||||
const int64_t kSegment1Start = kSegment0Start + kSegmentDuration;
|
const int64_t kSegment1Start = 100;
|
||||||
|
|
||||||
const int64_t kSample0Start = 0;
|
const int64_t kSampleAStart = 25;
|
||||||
const int64_t kSample0Duration = kTick;
|
const int64_t kSampleAEnd = 75;
|
||||||
const int64_t kSample0End = kSample0Start + kSample0Duration;
|
|
||||||
|
|
||||||
const int64_t kSample1Start = 3 * kTick;
|
const int64_t kSampleBStart = 125;
|
||||||
const int64_t kSample1Duration = kTick;
|
const int64_t kSampleBEnd = 175;
|
||||||
const int64_t kSample1End = kSample1Start + kSample1Duration;
|
|
||||||
|
|
||||||
Init(kSegmentDuration);
|
Init(kSegmentDurationSec);
|
||||||
|
|
||||||
{
|
{
|
||||||
testing::InSequence s;
|
testing::InSequence s;
|
||||||
|
@ -106,21 +121,21 @@ TEST_F(TextChunkerTest, CreatesSegmentsForSamples) {
|
||||||
|
|
||||||
// Segment One
|
// Segment One
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
// Segment Two
|
// Segment Two
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSample1Start, kSample1End,
|
OnProcess(IsTextSample(kNoId, kSampleBStart, kSampleBEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
||||||
}
|
}
|
||||||
|
@ -129,33 +144,40 @@ TEST_F(TextChunkerTest, CreatesSegmentsForSamples) {
|
||||||
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
|
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
kStreamIndex,
|
kStreamIndex,
|
||||||
GetTextSample(kNoId, kSample0Start, kSample0End, kNoPayload))));
|
GetTextSample(kNoId, kSampleAStart, kSampleAEnd, kNoPayload))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
kStreamIndex,
|
kStreamIndex,
|
||||||
GetTextSample(kNoId, kSample1Start, kSample1End, kNoPayload))));
|
GetTextSample(kNoId, kSampleBStart, kSampleBEnd, kNoPayload))));
|
||||||
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
||||||
}
|
}
|
||||||
|
|
||||||
// S0 S1 S2 S3
|
// Verify that a segment will get outputted even if there are no samples
|
||||||
// | | | |
|
// overlapping with it.
|
||||||
// |[-A-] | | |
|
//
|
||||||
// | | |[-B-] |
|
// Segment Duration = 100 MS
|
||||||
// | | | |
|
//
|
||||||
|
// TIME (ms):0 5 1 1 2 2 3
|
||||||
|
// 0 0 5 0 5 0
|
||||||
|
// 0 0 0 0 0
|
||||||
|
// SAMPLES : [--A--]
|
||||||
|
// [--B--]
|
||||||
|
// SEGMENTS : ^ ^ ^
|
||||||
|
//
|
||||||
TEST_F(TextChunkerTest, OutputsEmptySegments) {
|
TEST_F(TextChunkerTest, OutputsEmptySegments) {
|
||||||
const int64_t kSegmentDuration = 2 * kTick;
|
const double kSegmentDurationSec = 0.1;
|
||||||
|
const int64_t kSegmentDurationMs = 100;
|
||||||
|
|
||||||
const int64_t kSegment0Start = 0;
|
const int64_t kSegment0Start = 0;
|
||||||
const int64_t kSegment1Start = kSegment0Start + kSegmentDuration;
|
const int64_t kSegment1Start = 100;
|
||||||
const int64_t kSegment2Start = kSegment1Start + kSegmentDuration;
|
const int64_t kSegment2Start = 200;
|
||||||
|
|
||||||
const int64_t kSample0Start = 0;
|
const int64_t kSampleAStart = 25;
|
||||||
const int64_t kSample0Duration = kTick;
|
const int64_t kSampleAEnd = 75;
|
||||||
const int64_t kSample0End = kSample0Start + kSample0Duration;
|
|
||||||
|
|
||||||
const int64_t kSample1Start = 4 * kTick;
|
const int64_t kSampleBStart = 225;
|
||||||
const int64_t kSample1Duration = kTick;
|
const int64_t kSampleBEnd = 275;
|
||||||
const int64_t kSample1End = kSample1Start + kSample1Duration;
|
|
||||||
|
|
||||||
Init(kSegmentDuration);
|
Init(kSegmentDurationSec);
|
||||||
|
|
||||||
{
|
{
|
||||||
testing::InSequence s;
|
testing::InSequence s;
|
||||||
|
@ -164,27 +186,27 @@ TEST_F(TextChunkerTest, OutputsEmptySegments) {
|
||||||
|
|
||||||
// Segment One
|
// Segment One
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
// Segment Two (empty segment)
|
// Segment Two (empty segment)
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
// Segment Three
|
// Segment Three
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSample1Start, kSample1End,
|
OnProcess(IsTextSample(kNoId, kSampleBStart, kSampleBEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
||||||
}
|
}
|
||||||
|
@ -193,27 +215,35 @@ TEST_F(TextChunkerTest, OutputsEmptySegments) {
|
||||||
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
|
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
kStreamIndex,
|
kStreamIndex,
|
||||||
GetTextSample(kNoId, kSample0Start, kSample0End, kNoPayload))));
|
GetTextSample(kNoId, kSampleAStart, kSampleAEnd, kNoPayload))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
kStreamIndex,
|
kStreamIndex,
|
||||||
GetTextSample(kNoId, kSample1Start, kSample1End, kNoPayload))));
|
GetTextSample(kNoId, kSampleBStart, kSampleBEnd, kNoPayload))));
|
||||||
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
||||||
}
|
}
|
||||||
|
|
||||||
// S0 S1 S2
|
// Verify that samples that overlap multiple samples get dispatch in all
|
||||||
// | | |
|
// segments.
|
||||||
// | [---A---] |
|
//
|
||||||
// | | |
|
// Segment Duration = 100 MS
|
||||||
|
//
|
||||||
|
// TIME (ms):0 5 1 1
|
||||||
|
// 0 0 5
|
||||||
|
// 0 0
|
||||||
|
// SAMPLES : [-----A-----]
|
||||||
|
// SEGMENTS : ^
|
||||||
|
//
|
||||||
TEST_F(TextChunkerTest, SampleCrossesSegments) {
|
TEST_F(TextChunkerTest, SampleCrossesSegments) {
|
||||||
const int64_t kSegmentDuration = 2 * kTick;
|
const double kSegmentDurationSec = 0.1;
|
||||||
|
const int64_t kSegmentDurationMs = 100;
|
||||||
|
|
||||||
const int64_t kSegment0Start = 0;
|
const int64_t kSegment0Start = 0;
|
||||||
const int64_t kSegment1Start = kSegment0Start + kSegmentDuration;
|
const int64_t kSegment1Start = 100;
|
||||||
|
|
||||||
const int64_t kSampleStart = kTick;
|
const int64_t kSampleAStart = 50;
|
||||||
const int64_t kSampleDuration = 2 * kTick;
|
const int64_t kSampleAEnd = 150;
|
||||||
const int64_t kSampleEnd = kSampleStart + kSampleDuration;
|
|
||||||
|
|
||||||
Init(kSegmentDuration);
|
Init(kSegmentDurationSec);
|
||||||
|
|
||||||
{
|
{
|
||||||
testing::InSequence s;
|
testing::InSequence s;
|
||||||
|
@ -222,63 +252,68 @@ TEST_F(TextChunkerTest, SampleCrossesSegments) {
|
||||||
|
|
||||||
// Segment One
|
// Segment One
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSampleStart, kSampleEnd,
|
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
// Segment Two
|
// Segment Two
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSampleStart, kSampleEnd,
|
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromStreamInfo(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromStreamInfo(
|
||||||
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
|
kStreamIndex, GetTextStreamInfo(kTimescaleMs))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
kStreamIndex,
|
kStreamIndex,
|
||||||
GetTextSample(kNoId, kSampleStart, kSampleEnd, kNoPayload))));
|
GetTextSample(kNoId, kSampleAStart, kSampleAEnd, kNoPayload))));
|
||||||
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
||||||
}
|
}
|
||||||
|
|
||||||
// S0 S1 S2 S3
|
// Verify that samples that overlap multiple samples get dispatch in all
|
||||||
// | | | |
|
// segments, even if different samples elapse different number of segments.
|
||||||
// | [-A----|----] | |
|
//
|
||||||
// | [-B----|----] | |
|
// Segment Duration = 100 MS
|
||||||
// | [-C----|----------|----] |
|
//
|
||||||
// | | | |
|
// TIME (ms):0 5 1 1 2 2 3
|
||||||
|
// 0 0 5 0 5 0
|
||||||
|
// 0 0 0 0 0
|
||||||
|
// SAMPLES : [-----A-----]
|
||||||
|
// [-----B-----]
|
||||||
|
// [-----------C-----------]
|
||||||
|
// SEGMENTS : ^ ^ ^
|
||||||
|
//
|
||||||
TEST_F(TextChunkerTest, PreservesOrder) {
|
TEST_F(TextChunkerTest, PreservesOrder) {
|
||||||
const int64_t kSegmentDuration = 2 * kTick;
|
const double kSegmentDurationSec = 0.1;
|
||||||
|
const int64_t kSegmentDurationMs = 100;
|
||||||
|
|
||||||
const int64_t kSegment0Start = 0;
|
const int64_t kSegment0Start = 0;
|
||||||
const int64_t kSegment1Start = kSegment0Start + kSegmentDuration;
|
const int64_t kSegment1Start = 100;
|
||||||
const int64_t kSegment2Start = kSegment1Start + kSegmentDuration;
|
const int64_t kSegment2Start = 200;
|
||||||
|
|
||||||
const int64_t kSample0Start = kTick;
|
const int64_t kSampleAStart = 50;
|
||||||
const int64_t kSample0Duration = 2 * kTick;
|
const int64_t kSampleAEnd = 150;
|
||||||
const int64_t kSample0End = kSample0Start + kSample0Duration;
|
|
||||||
|
|
||||||
const int64_t kSample1Start = kTick;
|
const int64_t kSampleBStart = 50;
|
||||||
const int64_t kSample1Duration = 2 * kTick;
|
const int64_t kSampleBEnd = 150;
|
||||||
const int64_t kSample1End = kSample1Start + kSample1Duration;
|
|
||||||
|
|
||||||
const int64_t kSample2Start = kTick;
|
const int64_t kSampleCStart = 50;
|
||||||
const int64_t kSample2Duration = 4 * kTick;
|
const int64_t kSampleCEnd = 250;
|
||||||
const int64_t kSample2End = kSample2Start + kSample2Duration;
|
|
||||||
|
|
||||||
const char* kSample0Id = "sample 0";
|
const char* kSampleAId = "sample 0";
|
||||||
const char* kSample1Id = "sample 1";
|
const char* kSampleBId = "sample 1";
|
||||||
const char* kSample2Id = "sample 2";
|
const char* kSampleCId = "sample 2";
|
||||||
|
|
||||||
Init(kSegmentDuration);
|
Init(kSegmentDurationSec);
|
||||||
|
|
||||||
{
|
{
|
||||||
testing::InSequence s;
|
testing::InSequence s;
|
||||||
|
@ -287,42 +322,42 @@ TEST_F(TextChunkerTest, PreservesOrder) {
|
||||||
|
|
||||||
// Segment One
|
// Segment One
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kSample0Id, kSample0Start, kSample0End,
|
OnProcess(IsTextSample(kSampleAId, kSampleAStart, kSampleAEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kSample1Id, kSample1Start, kSample1End,
|
OnProcess(IsTextSample(kSampleBId, kSampleBStart, kSampleBEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kSample2Id, kSample2Start, kSample2End,
|
OnProcess(IsTextSample(kSampleCId, kSampleCStart, kSampleCEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
// Segment Two
|
// Segment Two
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kSample0Id, kSample0Start, kSample0End,
|
OnProcess(IsTextSample(kSampleAId, kSampleAStart, kSampleAEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kSample1Id, kSample1Start, kSample1End,
|
OnProcess(IsTextSample(kSampleBId, kSampleBStart, kSampleBEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kSample2Id, kSample2Start, kSample2End,
|
OnProcess(IsTextSample(kSampleCId, kSampleCStart, kSampleCEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
// Segment Two
|
// Segment Two
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kSample2Id, kSample2Start, kSample2End,
|
OnProcess(IsTextSample(kSampleCId, kSampleCStart, kSampleCEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
||||||
}
|
}
|
||||||
|
@ -331,39 +366,45 @@ TEST_F(TextChunkerTest, PreservesOrder) {
|
||||||
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
|
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
kStreamIndex,
|
kStreamIndex,
|
||||||
GetTextSample(kSample0Id, kSample0Start, kSample0End, kNoPayload))));
|
GetTextSample(kSampleAId, kSampleAStart, kSampleAEnd, kNoPayload))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
kStreamIndex,
|
kStreamIndex,
|
||||||
GetTextSample(kSample1Id, kSample1Start, kSample1End, kNoPayload))));
|
GetTextSample(kSampleBId, kSampleBStart, kSampleBEnd, kNoPayload))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
kStreamIndex,
|
kStreamIndex,
|
||||||
GetTextSample(kSample2Id, kSample2Start, kSample2End, kNoPayload))));
|
GetTextSample(kSampleCId, kSampleCStart, kSampleCEnd, kNoPayload))));
|
||||||
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
||||||
}
|
}
|
||||||
|
|
||||||
// S0 S1 S2 S3 S4 S5
|
// Check that when samples overlap/contain other samples, that they still
|
||||||
// | | | | | |
|
// get outputted in the correct segments.
|
||||||
// | [--|-----|--A--|-----|--] |
|
//
|
||||||
// | | [--|--B--|--] | |
|
// Segment Duration = 50 MS
|
||||||
// | | | | | |
|
//
|
||||||
|
// TIME (ms):0 5 1 1 2 2
|
||||||
|
// 0 0 5 0 5
|
||||||
|
// 0 0 0 0
|
||||||
|
// SAMPLES : [-----------A-----------]
|
||||||
|
// [-----B------]
|
||||||
|
// SEGMENTS : ^ ^ ^ ^ ^
|
||||||
|
//
|
||||||
TEST_F(TextChunkerTest, NestedSamples) {
|
TEST_F(TextChunkerTest, NestedSamples) {
|
||||||
const int64_t kSegmentDuration = 2 * kTick;
|
const double kSegmentDurationSec = 0.05;
|
||||||
|
const int64_t kSegmentDurationMs = 50;
|
||||||
|
|
||||||
const int64_t kSample0Start = 1 * kTick;
|
const int64_t kSampleAStart = 25;
|
||||||
const int64_t kSample0Duration = 8 * kTick;
|
const int64_t kSampleAEnd = 225;
|
||||||
const int64_t kSample0End = kSample0Start + kSample0Duration;
|
|
||||||
|
|
||||||
const int64_t kSample1Start = 3 * kTick;
|
const int64_t kSampleBStart = 75;
|
||||||
const int64_t kSample1Duration = 4 * kTick;
|
const int64_t kSampleBEnd = 175;
|
||||||
const int64_t kSample1End = kSample1Start + kSample1Duration;
|
|
||||||
|
|
||||||
const int64_t kSegment0Start = 0;
|
const int64_t kSegment0Start = 0;
|
||||||
const int64_t kSegment1Start = kSegment0Start + kSegmentDuration;
|
const int64_t kSegment1Start = 50;
|
||||||
const int64_t kSegment2Start = kSegment1Start + kSegmentDuration;
|
const int64_t kSegment2Start = 100;
|
||||||
const int64_t kSegment3Start = kSegment2Start + kSegmentDuration;
|
const int64_t kSegment3Start = 150;
|
||||||
const int64_t kSegment4Start = kSegment3Start + kSegmentDuration;
|
const int64_t kSegment4Start = 200;
|
||||||
|
|
||||||
Init(kSegmentDuration);
|
Init(kSegmentDurationSec);
|
||||||
|
|
||||||
{
|
{
|
||||||
testing::InSequence s;
|
testing::InSequence s;
|
||||||
|
@ -372,57 +413,57 @@ TEST_F(TextChunkerTest, NestedSamples) {
|
||||||
|
|
||||||
// Segment 0
|
// Segment 0
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
// Segment 1
|
// Segment 1
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSample1Start, kSample1End,
|
OnProcess(IsTextSample(kNoId, kSampleBStart, kSampleBEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
// Segment 2
|
// Segment 2
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSample1Start, kSample1End,
|
OnProcess(IsTextSample(kNoId, kSampleBStart, kSampleBEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
// Segment 3
|
// Segment 3
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSample1Start, kSample1End,
|
OnProcess(IsTextSample(kNoId, kSampleBStart, kSampleBEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment3Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment3Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
// Segment 4
|
// Segment 4
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment4Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment4Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
||||||
}
|
}
|
||||||
|
@ -431,33 +472,40 @@ TEST_F(TextChunkerTest, NestedSamples) {
|
||||||
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
|
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
kStreamIndex,
|
kStreamIndex,
|
||||||
GetTextSample(kNoId, kSample0Start, kSample0End, kNoPayload))));
|
GetTextSample(kNoId, kSampleAStart, kSampleAEnd, kNoPayload))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
kStreamIndex,
|
kStreamIndex,
|
||||||
GetTextSample(kNoId, kSample1Start, kSample1End, kNoPayload))));
|
GetTextSample(kNoId, kSampleBStart, kSampleBEnd, kNoPayload))));
|
||||||
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
||||||
}
|
}
|
||||||
|
|
||||||
// S0 S1 S2 S3
|
// Make sure that a sample that extends multiple segments is dropped when
|
||||||
// | | | |
|
// it no longer overlaps with a later segment.
|
||||||
// | [------A--------]| |
|
//
|
||||||
// | | |[--B--] |
|
// Segment Duration = 100 MS
|
||||||
// | | | |
|
//
|
||||||
|
// TIME (ms):0 5 1 1 2 2 3
|
||||||
|
// 0 0 5 0 5 0
|
||||||
|
// 0 0 0 0 0
|
||||||
|
// SAMPLES : [--------A--------]
|
||||||
|
// [--B--]
|
||||||
|
// SEGMENTS : ^ ^ ^
|
||||||
|
//
|
||||||
TEST_F(TextChunkerTest, SecondSampleStartsAfterMultiSegmentSampleEnds) {
|
TEST_F(TextChunkerTest, SecondSampleStartsAfterMultiSegmentSampleEnds) {
|
||||||
const int64_t kSegmentDuration = 2 * kTick;
|
const double kSegmentDurationSec = 0.1;
|
||||||
|
const int64_t kSegmentDurationMs = 100;
|
||||||
|
|
||||||
const int64_t kSegment0Start = 0;
|
const int64_t kSegment0Start = 0;
|
||||||
const int64_t kSegment1Start = kSegment0Start + kSegmentDuration;
|
const int64_t kSegment1Start = 100;
|
||||||
const int64_t kSegment2Start = kSegment1Start + kSegmentDuration;
|
const int64_t kSegment2Start = 200;
|
||||||
|
|
||||||
const int64_t kSample0Start = kTick;
|
const int64_t kSampleAStart = 50;
|
||||||
const int64_t kSample0Duration = 3 * kTick;
|
const int64_t kSampleAEnd = 200;
|
||||||
const int64_t kSample0End = kSample0Start + kSample0Duration;
|
|
||||||
|
|
||||||
const int64_t kSample1Start = 4 * kTick;
|
const int64_t kSampleBStart = 200;
|
||||||
const int64_t kSample1Duration = kTick;
|
const int64_t kSampleBEnd = 250;
|
||||||
const int64_t kSample1End = kSample1Start + kSample1Duration;
|
|
||||||
|
|
||||||
Init(kSegmentDuration);
|
Init(kSegmentDurationSec);
|
||||||
|
|
||||||
{
|
{
|
||||||
testing::InSequence s;
|
testing::InSequence s;
|
||||||
|
@ -466,72 +514,77 @@ TEST_F(TextChunkerTest, SecondSampleStartsAfterMultiSegmentSampleEnds) {
|
||||||
|
|
||||||
// Segment One
|
// Segment One
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
// Segment Two
|
// Segment Two
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
// Segment Three
|
// Segment Three
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSample1Start, kSample1End,
|
OnProcess(IsTextSample(kNoId, kSampleBStart, kSampleBEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start, kSegmentDuration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
|
|
||||||
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromStreamInfo(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromStreamInfo(
|
||||||
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
|
kStreamIndex, GetTextStreamInfo(kTimescaleMs))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
kStreamIndex,
|
kStreamIndex,
|
||||||
GetTextSample(kNoId, kSample0Start, kSample0End, kNoPayload))));
|
GetTextSample(kNoId, kSampleAStart, kSampleAEnd, kNoPayload))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
kStreamIndex,
|
kStreamIndex,
|
||||||
GetTextSample(kNoId, kSample1Start, kSample1End, kNoPayload))));
|
GetTextSample(kNoId, kSampleBStart, kSampleBEnd, kNoPayload))));
|
||||||
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
||||||
}
|
}
|
||||||
|
|
||||||
// S0 C0 C1 S1 * *
|
// Check that segments will be injected when a cue event comes down the
|
||||||
// s0 s1 s2 * * s3
|
// pipeline and that the segment duration will get reset after the cues
|
||||||
// | | | |
|
// are dispatched.
|
||||||
// | [---|-----A----|---] |
|
//
|
||||||
// | | | |
|
// Segment Duration = 300 MS
|
||||||
// The segment duration is 8 ticks, but with the cues being injected, c0 will
|
//
|
||||||
// become s1, c1 will become s3, and S1 will become s3.
|
// TIME (ms):0 5 1 1 2 2 3 3 4 5
|
||||||
|
// 0 0 5 0 5 0 5 5 0
|
||||||
|
// 0 0 0 0 0 0 0 0
|
||||||
|
// SAMPLES : [-----------A-----------]
|
||||||
|
// CUES : ^ ^
|
||||||
|
// SEGMENTS : ^ ^ ^
|
||||||
|
//
|
||||||
TEST_F(TextChunkerTest, SampleSpanningMultipleCues) {
|
TEST_F(TextChunkerTest, SampleSpanningMultipleCues) {
|
||||||
const int64_t kSegmentDuration = 8 * kTick;
|
const double kSegmentDurationSec = 0.3;
|
||||||
|
const int64_t kSegmentDurationMs = 300;
|
||||||
|
|
||||||
const int64_t kSampleStart = kTick;
|
const int64_t kSampleAStart = 50;
|
||||||
const int64_t kSampleDuration = 4 * kTick;
|
const int64_t kSampleAEnd = 250;
|
||||||
const int64_t kSampleEnd = kSampleStart + kSampleDuration;
|
|
||||||
|
|
||||||
const int64_t kCue0Time = 2 * kTick;
|
const double kC0 = 0.1;
|
||||||
const double kCue0TimeInSeconds = kCue0Time / 1000.0;
|
const double kC1 = 0.2;
|
||||||
const int64_t kCue1Time = 4 * kTick;
|
|
||||||
const double kCue1TimeInSeconds = kCue1Time / 1000.0;
|
|
||||||
|
|
||||||
const int64_t kSegment0Start = 0;
|
const int64_t kSegment0Start = 0;
|
||||||
const int64_t kSegment0Duration = 2 * kTick;
|
const int64_t kSegment1Start = 100;
|
||||||
const int64_t kSegment1Start = kSegment0Start + kSegment0Duration;
|
const int64_t kSegment2Start = 200;
|
||||||
const int64_t kSegment1Duration = 2 * kTick;
|
;
|
||||||
const int64_t kSegment2Start = kSegment1Start + kSegment1Duration;
|
|
||||||
const int64_t kSegment2Duration = 8 * kTick;
|
|
||||||
|
|
||||||
Init(kSegmentDuration);
|
const double kSegment0StartLength = 100;
|
||||||
|
const double kSegment1StartLength = 100;
|
||||||
|
|
||||||
|
Init(kSegmentDurationSec);
|
||||||
|
|
||||||
{
|
{
|
||||||
testing::InSequence s;
|
testing::InSequence s;
|
||||||
|
@ -540,45 +593,43 @@ TEST_F(TextChunkerTest, SampleSpanningMultipleCues) {
|
||||||
|
|
||||||
// Segment 0 and Cue 0
|
// Segment 0 and Cue 0
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSampleStart, kSampleEnd,
|
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
|
||||||
*Output(kOutput),
|
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start, kSegment0Duration,
|
|
||||||
!kSubSegment, !kEncrypted)));
|
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsCueEvent(kStreamIndex, kCue0TimeInSeconds)));
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start,
|
||||||
|
kSegment0StartLength, !kSubSegment,
|
||||||
|
!kEncrypted)));
|
||||||
|
EXPECT_CALL(*Output(kOutput), OnProcess(IsCueEvent(kStreamIndex, kC0)));
|
||||||
|
|
||||||
// Segment 1 and Cue 1
|
// Segment 1 and Cue 1
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSampleStart, kSampleEnd,
|
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
|
||||||
*Output(kOutput),
|
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start, kSegment1Duration,
|
|
||||||
!kSubSegment, !kEncrypted)));
|
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsCueEvent(kStreamIndex, kCue1TimeInSeconds)));
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start,
|
||||||
|
kSegment1StartLength, !kSubSegment,
|
||||||
|
!kEncrypted)));
|
||||||
|
EXPECT_CALL(*Output(kOutput), OnProcess(IsCueEvent(kStreamIndex, kC1)));
|
||||||
|
|
||||||
// Segment 2
|
// Segment 2
|
||||||
EXPECT_CALL(*Output(kOutput),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kNoId, kSampleStart, kSampleEnd,
|
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
|
||||||
kNoSettings, kNoPayload)));
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*Output(kOutput),
|
||||||
*Output(kOutput),
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start,
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start, kSegment2Duration,
|
kSegmentDurationMs, !kSubSegment,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kEncrypted)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromStreamInfo(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromStreamInfo(
|
||||||
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
|
kStreamIndex, GetTextStreamInfo(kTimescaleMs))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
kStreamIndex,
|
kStreamIndex,
|
||||||
GetTextSample(kNoId, kSampleStart, kSampleEnd, kNoPayload))));
|
GetTextSample(kNoId, kSampleAStart, kSampleAEnd, kNoPayload))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(
|
ASSERT_OK(Input(kInput)->Dispatch(
|
||||||
StreamData::FromCueEvent(kStreamIndex, GetCueEvent(kCue0TimeInSeconds))));
|
StreamData::FromCueEvent(kStreamIndex, GetCueEvent(kC0))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(
|
ASSERT_OK(Input(kInput)->Dispatch(
|
||||||
StreamData::FromCueEvent(kStreamIndex, GetCueEvent(kCue1TimeInSeconds))));
|
StreamData::FromCueEvent(kStreamIndex, GetCueEvent(kC1))));
|
||||||
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -456,10 +456,8 @@ std::unique_ptr<TextChunker> CreateTextChunker(
|
||||||
const ChunkingParams& chunking_params) {
|
const ChunkingParams& chunking_params) {
|
||||||
const float segment_length_in_seconds =
|
const float segment_length_in_seconds =
|
||||||
chunking_params.segment_duration_in_seconds;
|
chunking_params.segment_duration_in_seconds;
|
||||||
const uint64_t segment_length_in_ms =
|
return std::unique_ptr<TextChunker>(
|
||||||
static_cast<uint64_t>(segment_length_in_seconds * 1000);
|
new TextChunker(segment_length_in_seconds));
|
||||||
|
|
||||||
return std::unique_ptr<TextChunker>(new TextChunker(segment_length_in_ms));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Status CreateHlsTextJob(const StreamDescriptor& stream,
|
Status CreateHlsTextJob(const StreamDescriptor& stream,
|
||||||
|
|
Loading…
Reference in New Issue