Handle CueEvent in TextChunker
Updated TextChunker to handle incoming CueEvents. Connecting the text chunker with the cue alignment handler will happen in a later CL. Issue: #362 Change-Id: Ib1fa9f457cf4ec0ce413dadcfa7eed5895ecd628
This commit is contained in:
parent
daac6869fb
commit
a11cbf93a7
|
@ -6,26 +6,42 @@
|
||||||
|
|
||||||
#include "packager/media/chunking/text_chunker.h"
|
#include "packager/media/chunking/text_chunker.h"
|
||||||
|
|
||||||
|
#include "packager/status_macros.h"
|
||||||
|
|
||||||
namespace shaka {
|
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(uint64_t segment_duration_ms)
|
TextChunker::TextChunker(int64_t segment_duration_ms)
|
||||||
: segment_duration_ms_(segment_duration_ms) {}
|
: segment_duration_ms_(segment_duration_ms),
|
||||||
|
segment_start_ms_(0),
|
||||||
|
segment_expected_end_ms_(segment_duration_ms) {}
|
||||||
|
|
||||||
Status TextChunker::InitializeInternal() {
|
Status TextChunker::InitializeInternal() {
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status TextChunker::Process(std::unique_ptr<StreamData> stream_data) {
|
Status TextChunker::Process(std::unique_ptr<StreamData> data) {
|
||||||
switch (stream_data->stream_data_type) {
|
switch (data->stream_data_type) {
|
||||||
case StreamDataType::kStreamInfo:
|
case StreamDataType::kStreamInfo:
|
||||||
return DispatchStreamInfo(kStreamIndex,
|
return OnStreamInfo(std::move(data->stream_info));
|
||||||
std::move(stream_data->stream_info));
|
|
||||||
case StreamDataType::kTextSample:
|
case StreamDataType::kTextSample:
|
||||||
return OnTextSample(stream_data->text_sample);
|
return OnTextSample(data->text_sample);
|
||||||
|
case StreamDataType::kCueEvent:
|
||||||
|
return OnCueEvent(data->cue_event);
|
||||||
default:
|
default:
|
||||||
return Status(error::INTERNAL_ERROR,
|
return Status(error::INTERNAL_ERROR,
|
||||||
"Invalid stream data type for this handler");
|
"Invalid stream data type for this handler");
|
||||||
|
@ -33,92 +49,75 @@ Status TextChunker::Process(std::unique_ptr<StreamData> stream_data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Status TextChunker::OnFlushRequest(size_t input_stream_index) {
|
Status TextChunker::OnFlushRequest(size_t input_stream_index) {
|
||||||
// At this point we know that there is a single series of consecutive
|
// Keep outputting segments until all the samples leave the system.
|
||||||
// segments, all we need to do is run through all of them.
|
while (segment_samples_.size()) {
|
||||||
for (const auto& pair : segment_map_) {
|
RETURN_IF_ERROR(EndSegment(segment_expected_end_ms_));
|
||||||
Status status = DispatchSegmentWithSamples(pair.first, pair.second);
|
|
||||||
|
|
||||||
if (!status.ok()) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
segment_map_.clear();
|
|
||||||
|
|
||||||
return FlushAllDownstreams();
|
return FlushAllDownstreams();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status TextChunker::OnTextSample(std::shared_ptr<const TextSample> sample) {
|
Status TextChunker::OnStreamInfo(std::shared_ptr<const StreamInfo> info) {
|
||||||
const uint64_t start_segment = sample->start_time() / segment_duration_ms_;
|
// There is no information we need from the stream info, so just pass it
|
||||||
|
// downstream.
|
||||||
|
return DispatchStreamInfo(kStreamIndex, std::move(info));
|
||||||
|
}
|
||||||
|
|
||||||
// Find the last segment that overlaps the sample. Adjust the sample by one
|
Status TextChunker::OnCueEvent(std::shared_ptr<const CueEvent> event) {
|
||||||
// ms (smallest time unit) in case |EndTime| falls on the segment boundary.
|
// We are going to cut the current segment into two using the event's time as
|
||||||
DCHECK_GT(sample->duration(), 0u);
|
// the division.
|
||||||
const uint64_t ending_segment =
|
const int64_t cue_time_in_ms = event->time_in_seconds * 1000;
|
||||||
(sample->EndTime() - 1) / segment_duration_ms_;
|
|
||||||
|
|
||||||
DCHECK_GE(ending_segment, start_segment);
|
// In the case that there is a gap with no samples between the last sample
|
||||||
|
// and the cue event, output all the segments until we get to the segment that
|
||||||
// Samples must always be advancing. If a sample comes in out of order,
|
// the cue event interrupts.
|
||||||
// skip the sample.
|
while (segment_expected_end_ms_ < cue_time_in_ms) {
|
||||||
if (head_segment_ > start_segment) {
|
RETURN_IF_ERROR(EndSegment(segment_expected_end_ms_));
|
||||||
LOG(WARNING) << "New sample has arrived out of order. Skipping sample "
|
|
||||||
<< "as segment start is " << start_segment << " and segment "
|
|
||||||
<< "head is " << head_segment_ << ".";
|
|
||||||
return Status::OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the sample to each segment it spans.
|
RETURN_IF_ERROR(EndSegment(cue_time_in_ms));
|
||||||
for (uint64_t segment = start_segment; segment <= ending_segment; segment++) {
|
RETURN_IF_ERROR(DispatchCueEvent(kStreamIndex, std::move(event)));
|
||||||
segment_map_[segment].push_back(sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move forward segment-by-segment so that we output empty segments to fill
|
|
||||||
// any segments with no cues.
|
|
||||||
for (uint64_t segment = head_segment_; segment < start_segment; segment++) {
|
|
||||||
auto it = segment_map_.find(segment);
|
|
||||||
|
|
||||||
Status status;
|
|
||||||
if (it == segment_map_.end()) {
|
|
||||||
const SegmentSamples kNoSamples;
|
|
||||||
status.Update(DispatchSegmentWithSamples(segment, kNoSamples));
|
|
||||||
} else {
|
|
||||||
// We found a segment, output all the samples. Remove it from the map as
|
|
||||||
// we should never need to write to it again.
|
|
||||||
status.Update(DispatchSegmentWithSamples(segment, it->second));
|
|
||||||
segment_map_.erase(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we fail to output a single sample, just stop.
|
|
||||||
if (!status.ok()) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Jump ahead to the start of this segment as we should never have any samples
|
|
||||||
// start before |start_segment|.
|
|
||||||
head_segment_ = start_segment;
|
|
||||||
|
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status TextChunker::DispatchSegmentWithSamples(uint64_t segment,
|
Status TextChunker::OnTextSample(std::shared_ptr<const TextSample> sample) {
|
||||||
const SegmentSamples& samples) {
|
// Output all segments that come before our new sample.
|
||||||
Status status;
|
while (segment_expected_end_ms_ <= sample->start_time()) {
|
||||||
for (const auto& sample : samples) {
|
RETURN_IF_ERROR(EndSegment(segment_expected_end_ms_));
|
||||||
status.Update(DispatchTextSample(kStreamIndex, sample));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only send the segment info if all the samples were successful.
|
segment_samples_.push_back(std::move(sample));
|
||||||
if (!status.ok()) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<SegmentInfo> info = std::make_shared<SegmentInfo>();
|
return Status::OK;
|
||||||
info->start_timestamp = segment * segment_duration_ms_;
|
|
||||||
info->duration = segment_duration_ms_;
|
|
||||||
|
|
||||||
return DispatchSegmentInfo(kStreamIndex, std::move(info));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status TextChunker::EndSegment(int64_t segment_actual_end_ms) {
|
||||||
|
// Output all the samples that are part of the segment.
|
||||||
|
for (const auto& sample : segment_samples_) {
|
||||||
|
RETURN_IF_ERROR(DispatchTextSample(kStreamIndex, sample));
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_IF_ERROR(DispatchSegmentInfo(
|
||||||
|
kStreamIndex, MakeSegmentInfo(segment_start_ms_, segment_actual_end_ms)));
|
||||||
|
|
||||||
|
// Create a new segment that comes right after the old segment and remove all
|
||||||
|
// samples that don't cross over into the new segment.
|
||||||
|
StartNewSegment(segment_actual_end_ms);
|
||||||
|
|
||||||
|
return Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextChunker::StartNewSegment(int64_t start_ms) {
|
||||||
|
segment_start_ms_ = start_ms;
|
||||||
|
segment_expected_end_ms_ = start_ms + segment_duration_ms_;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
|
@ -7,43 +7,41 @@
|
||||||
#ifndef PACKAGER_MEDIA_CHUNKING_TEXT_CHUNKER_H_
|
#ifndef PACKAGER_MEDIA_CHUNKING_TEXT_CHUNKER_H_
|
||||||
#define PACKAGER_MEDIA_CHUNKING_TEXT_CHUNKER_H_
|
#define PACKAGER_MEDIA_CHUNKING_TEXT_CHUNKER_H_
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <list>
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "packager/media/base/media_handler.h"
|
#include "packager/media/base/media_handler.h"
|
||||||
#include "packager/status.h"
|
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
class TextChunker : public MediaHandler {
|
class TextChunker : public MediaHandler {
|
||||||
public:
|
public:
|
||||||
explicit TextChunker(uint64_t segment_duration_ms);
|
explicit TextChunker(int64_t segment_duration_ms);
|
||||||
|
|
||||||
protected:
|
|
||||||
Status Process(std::unique_ptr<StreamData> stream_data) override;
|
|
||||||
Status OnFlushRequest(size_t input_stream_index) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TextChunker(const TextChunker&) = delete;
|
TextChunker(const TextChunker&) = delete;
|
||||||
TextChunker& operator=(const TextChunker&) = delete;
|
TextChunker& operator=(const TextChunker&) = delete;
|
||||||
|
|
||||||
using SegmentSamples = std::vector<std::shared_ptr<const TextSample>>;
|
|
||||||
|
|
||||||
Status InitializeInternal() override;
|
Status InitializeInternal() override;
|
||||||
|
|
||||||
|
Status Process(std::unique_ptr<StreamData> stream_data) override;
|
||||||
|
Status OnFlushRequest(size_t input_stream_index) override;
|
||||||
|
|
||||||
|
Status OnStreamInfo(std::shared_ptr<const StreamInfo> info);
|
||||||
|
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 DispatchSegmentWithSamples(uint64_t segment,
|
Status EndSegment(int64_t segment_actual_end_ms);
|
||||||
const SegmentSamples& samples);
|
void StartNewSegment(int64_t start_ms);
|
||||||
|
|
||||||
uint64_t segment_duration_ms_;
|
int64_t segment_duration_ms_;
|
||||||
|
|
||||||
// Mapping of segment number to segment.
|
// The segment that we are currently outputting samples for. The segment
|
||||||
std::map<uint64_t, SegmentSamples> segment_map_;
|
// will end once a new sample with start time greater or equal to the
|
||||||
uint64_t head_segment_ = 0;
|
// segment's end time arrives.
|
||||||
|
int64_t segment_start_ms_;
|
||||||
|
int64_t segment_expected_end_ms_;
|
||||||
|
std::list<std::shared_ptr<const TextSample>> segment_samples_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
|
@ -15,298 +15,570 @@ namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const int64_t kStartTime = 0;
|
const uint64_t kStreamIndex = 0;
|
||||||
const int64_t kSegmentDuration = 10000; // 10 seconds
|
|
||||||
|
|
||||||
const size_t kStreamIndex = 0;
|
const size_t kInputs = 1;
|
||||||
|
const size_t kOutputs = 1;
|
||||||
|
|
||||||
const size_t kInputCount = 1;
|
const size_t kInput = 0;
|
||||||
const size_t kOutputCount = 1;
|
const size_t kOutput = 0;
|
||||||
const size_t kInputIndex = 0;
|
|
||||||
const size_t kOutputIndex = 0;
|
|
||||||
|
|
||||||
const bool kEncrypted = true;
|
const bool kEncrypted = true;
|
||||||
const bool kSubSegment = true;
|
const bool kSubSegment = true;
|
||||||
|
|
||||||
const char* kId[] = {"cue 1 id", "cue 2 id"};
|
const int64_t kTick = 50;
|
||||||
const char* kPayload[] = {"cue 1 payload", "cue 2 payload"};
|
|
||||||
|
|
||||||
const std::string kNoSettings = "";
|
const char* kNoId = "";
|
||||||
|
const char* kNoSettings = "";
|
||||||
|
const char* kNoPayload = "";
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class TextChunkerTest : public MediaHandlerTestBase {
|
class TextChunkerTest : public MediaHandlerTestBase {
|
||||||
protected:
|
protected:
|
||||||
void SetUp() {
|
void Init(int64_t segment_duration) {
|
||||||
ASSERT_OK(
|
ASSERT_OK(SetUpAndInitializeGraph(
|
||||||
SetUpAndInitializeGraph(std::make_shared<TextChunker>(kSegmentDuration),
|
std::make_shared<TextChunker>(segment_duration), kInputs, kOutputs));
|
||||||
kInputCount, kOutputCount));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// When a cue ends on a segment boundry, it does not create a cue with a 0 ms
|
// S0 S1
|
||||||
// duration
|
|
||||||
// | |
|
// | |
|
||||||
// |[---A---]|
|
// |[---A---]|
|
||||||
// | |
|
// | |
|
||||||
TEST_F(TextChunkerTest, CueEndingOnSegmentStart) {
|
TEST_F(TextChunkerTest, SampleEndingOnSegmentStart) {
|
||||||
const int64_t kSampleDuration = kSegmentDuration;
|
const int64_t kSegmentStart = 0;
|
||||||
|
const int64_t kSegmentDuration = kTick;
|
||||||
|
|
||||||
|
const int64_t kSampleStart = 0;
|
||||||
|
const int64_t kSampleDuration = kTick;
|
||||||
|
const int64_t kSampleEnd = kSampleStart + kSampleDuration;
|
||||||
|
|
||||||
|
Init(kSegmentDuration);
|
||||||
|
|
||||||
{
|
{
|
||||||
testing::InSequence s;
|
testing::InSequence s;
|
||||||
|
|
||||||
EXPECT_CALL(*Output(kOutputIndex), OnProcess(IsStreamInfo(kStreamIndex)));
|
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
|
||||||
|
|
||||||
// Segment One
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSampleStart, kSampleEnd,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(
|
||||||
*Output(kOutputIndex),
|
*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kId[0], kStartTime, kStartTime + kSampleDuration,
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegmentStart, kSegmentDuration,
|
||||||
kNoSettings, kPayload[0])));
|
|
||||||
EXPECT_CALL(
|
|
||||||
*Output(kOutputIndex),
|
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kStartTime, kSegmentDuration,
|
|
||||||
!kSubSegment, !kEncrypted)));
|
!kSubSegment, !kEncrypted)));
|
||||||
EXPECT_CALL(*Output(kOutputIndex), OnFlush(kStreamIndex));
|
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT_OK(Input(kInputIndex)
|
ASSERT_OK(Input(kInput)->Dispatch(
|
||||||
->Dispatch(StreamData::FromStreamInfo(kStreamIndex,
|
StreamData::FromStreamInfo(kStreamIndex, GetTextStreamInfo())));
|
||||||
GetTextStreamInfo())));
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
ASSERT_OK(Input(kInputIndex)
|
kStreamIndex,
|
||||||
->Dispatch(StreamData::FromTextSample(
|
GetTextSample(kNoId, kSampleStart, kSampleEnd, kNoPayload))));
|
||||||
kStreamIndex,
|
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
||||||
GetTextSample(kId[0], kStartTime,
|
|
||||||
kStartTime + kSampleDuration, kPayload[0]))));
|
|
||||||
ASSERT_OK(Input(kInputIndex)->FlushAllDownstreams());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each cue belongs in its own segment, so before each cue is passed
|
// S0 S1 S2
|
||||||
// downstream, a 'input of segment' message should be passed downstream.
|
// | | |
|
||||||
// |
|
// |[-A-] | |
|
||||||
// [---A---] |
|
// | |[-B-] |
|
||||||
// | [---B---]
|
// | | |
|
||||||
// |
|
TEST_F(TextChunkerTest, CreatesSegmentsForSamples) {
|
||||||
TEST_F(TextChunkerTest, CreatesSegmentsForCues) {
|
const int64_t kSegmentDuration = 2 * kTick;
|
||||||
// Divide segment duration by 2 so that the sample duration won't be a full
|
const int64_t kSegment0Start = 0;
|
||||||
// segment.
|
const int64_t kSegment1Start = kSegment0Start + kSegmentDuration;
|
||||||
const int64_t kSampleDuration = kSegmentDuration / 2;
|
|
||||||
|
const int64_t kSample0Start = 0;
|
||||||
|
const int64_t kSample0Duration = kTick;
|
||||||
|
const int64_t kSample0End = kSample0Start + kSample0Duration;
|
||||||
|
|
||||||
|
const int64_t kSample1Start = 3 * kTick;
|
||||||
|
const int64_t kSample1Duration = kTick;
|
||||||
|
const int64_t kSample1End = kSample1Start + kSample1Duration;
|
||||||
|
|
||||||
|
Init(kSegmentDuration);
|
||||||
|
|
||||||
{
|
{
|
||||||
testing::InSequence s;
|
testing::InSequence s;
|
||||||
|
|
||||||
EXPECT_CALL(*Output(kOutputIndex), OnProcess(IsStreamInfo(kStreamIndex)));
|
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
|
||||||
|
|
||||||
// Segment One
|
// Segment One
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(
|
||||||
*Output(kOutputIndex),
|
*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kId[0], kStartTime, kStartTime + kSampleDuration,
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start, kSegmentDuration,
|
||||||
kNoSettings, kPayload[0])));
|
|
||||||
EXPECT_CALL(
|
|
||||||
*Output(kOutputIndex),
|
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kStartTime, kSegmentDuration,
|
|
||||||
!kSubSegment, !kEncrypted)));
|
!kSubSegment, !kEncrypted)));
|
||||||
|
|
||||||
// Segment Two
|
// Segment Two
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSample1Start, kSample1End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(
|
||||||
*Output(kOutputIndex),
|
*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kId[1], kStartTime + kSegmentDuration,
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start, kSegmentDuration,
|
||||||
kStartTime + kSegmentDuration + kSampleDuration,
|
!kSubSegment, !kEncrypted)));
|
||||||
kNoSettings, kPayload[1])));
|
|
||||||
EXPECT_CALL(
|
|
||||||
*Output(kOutputIndex),
|
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kStartTime + kSegmentDuration,
|
|
||||||
kSegmentDuration, !kSubSegment, !kEncrypted)));
|
|
||||||
|
|
||||||
EXPECT_CALL(*Output(kOutputIndex), OnFlush(kStreamIndex));
|
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT_OK(Input(kInputIndex)
|
ASSERT_OK(Input(kInput)->Dispatch(
|
||||||
->Dispatch(StreamData::FromStreamInfo(kStreamIndex,
|
StreamData::FromStreamInfo(kStreamIndex, GetTextStreamInfo())));
|
||||||
GetTextStreamInfo())));
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
ASSERT_OK(Input(kInputIndex)
|
kStreamIndex,
|
||||||
->Dispatch(StreamData::FromTextSample(
|
GetTextSample(kNoId, kSample0Start, kSample0End, kNoPayload))));
|
||||||
kStreamIndex,
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
GetTextSample(kId[0], kStartTime,
|
kStreamIndex,
|
||||||
kStartTime + kSampleDuration, kPayload[0]))));
|
GetTextSample(kNoId, kSample1Start, kSample1End, kNoPayload))));
|
||||||
ASSERT_OK(
|
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
||||||
Input(kInputIndex)
|
|
||||||
->Dispatch(StreamData::FromTextSample(
|
|
||||||
kStreamIndex,
|
|
||||||
GetTextSample(kId[1], kStartTime + kSegmentDuration,
|
|
||||||
kStartTime + kSegmentDuration + kSampleDuration,
|
|
||||||
kPayload[1]))));
|
|
||||||
ASSERT_OK(Input(kInputIndex)->FlushAllDownstreams());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// [---A---] | |
|
// S0 S1 S2 S3
|
||||||
// | |
|
// | | | |
|
||||||
// | | [---B---]
|
// |[-A-] | | |
|
||||||
// | |
|
// | | |[-B-] |
|
||||||
|
// | | | |
|
||||||
TEST_F(TextChunkerTest, OutputsEmptySegments) {
|
TEST_F(TextChunkerTest, OutputsEmptySegments) {
|
||||||
const int64_t kSampleDuration = kSegmentDuration / 2;
|
const int64_t kSegmentDuration = 2 * kTick;
|
||||||
|
const int64_t kSegment0Start = 0;
|
||||||
const int64_t kSegment1Start = kStartTime;
|
const int64_t kSegment1Start = kSegment0Start + kSegmentDuration;
|
||||||
const int64_t kSegment2Start = kSegment1Start + kSegmentDuration;
|
const int64_t kSegment2Start = kSegment1Start + kSegmentDuration;
|
||||||
const int64_t kSegment3Start = kSegment2Start + kSegmentDuration;
|
|
||||||
|
const int64_t kSample0Start = 0;
|
||||||
|
const int64_t kSample0Duration = kTick;
|
||||||
|
const int64_t kSample0End = kSample0Start + kSample0Duration;
|
||||||
|
|
||||||
|
const int64_t kSample1Start = 4 * kTick;
|
||||||
|
const int64_t kSample1Duration = kTick;
|
||||||
|
const int64_t kSample1End = kSample1Start + kSample1Duration;
|
||||||
|
|
||||||
|
Init(kSegmentDuration);
|
||||||
|
|
||||||
{
|
{
|
||||||
testing::InSequence s;
|
testing::InSequence s;
|
||||||
|
|
||||||
EXPECT_CALL(*Output(kOutputIndex), OnProcess(IsStreamInfo(kStreamIndex)));
|
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
|
||||||
|
|
||||||
// Segment One
|
// Segment One
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(
|
||||||
*Output(kOutputIndex),
|
*Output(kOutput),
|
||||||
OnProcess(IsTextSample(kId[0], kStartTime, kStartTime + kSampleDuration,
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start, kSegmentDuration,
|
||||||
kNoSettings, kPayload[0])));
|
|
||||||
EXPECT_CALL(
|
|
||||||
*Output(kOutputIndex),
|
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start, kSegmentDuration,
|
|
||||||
!kSubSegment, !kEncrypted)));
|
!kSubSegment, !kEncrypted)));
|
||||||
|
|
||||||
// Segment Two (empty segment)
|
// Segment Two (empty segment)
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(
|
||||||
*Output(kOutputIndex),
|
*Output(kOutput),
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start, kSegmentDuration,
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start, kSegmentDuration,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kSubSegment, !kEncrypted)));
|
||||||
|
|
||||||
// Segment Three
|
// Segment Three
|
||||||
EXPECT_CALL(*Output(kOutputIndex),
|
EXPECT_CALL(*Output(kOutput),
|
||||||
OnProcess(IsTextSample(
|
OnProcess(IsTextSample(kNoId, kSample1Start, kSample1End,
|
||||||
kId[1], kStartTime + 2 * kSegmentDuration,
|
kNoSettings, kNoPayload)));
|
||||||
kStartTime + 2 * kSegmentDuration + kSampleDuration,
|
|
||||||
kNoSettings, kPayload[1])));
|
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(
|
||||||
*Output(kOutputIndex),
|
*Output(kOutput),
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kSegment3Start, kSegmentDuration,
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start, kSegmentDuration,
|
||||||
!kSubSegment, !kEncrypted)));
|
!kSubSegment, !kEncrypted)));
|
||||||
|
|
||||||
EXPECT_CALL(*Output(kOutputIndex), OnFlush(kStreamIndex));
|
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_OK(Input(kInputIndex)
|
|
||||||
->Dispatch(StreamData::FromStreamInfo(kStreamIndex,
|
|
||||||
GetTextStreamInfo())));
|
|
||||||
ASSERT_OK(Input(kInputIndex)
|
|
||||||
->Dispatch(StreamData::FromTextSample(
|
|
||||||
kStreamIndex,
|
|
||||||
GetTextSample(kId[0], kStartTime,
|
|
||||||
kStartTime + kSampleDuration, kPayload[0]))));
|
|
||||||
ASSERT_OK(
|
|
||||||
Input(kInputIndex)
|
|
||||||
->Dispatch(StreamData::FromTextSample(
|
|
||||||
kStreamIndex,
|
|
||||||
GetTextSample(kId[1], kStartTime + 2 * kSegmentDuration,
|
|
||||||
kStartTime + 2 * kSegmentDuration + kSampleDuration,
|
|
||||||
kPayload[1]))));
|
|
||||||
ASSERT_OK(Input(kInputIndex)->FlushAllDownstreams());
|
|
||||||
}
|
|
||||||
|
|
||||||
// When a cue crossing the segment boundary, the cue should be included in
|
|
||||||
// both segments.
|
|
||||||
// |
|
|
||||||
// [-----A-----|---------]
|
|
||||||
// |
|
|
||||||
TEST_F(TextChunkerTest, CueCrossesSegments) {
|
|
||||||
const int64_t kSampleDuration = 2 * kSegmentDuration;
|
|
||||||
|
|
||||||
{
|
|
||||||
testing::InSequence s;
|
|
||||||
|
|
||||||
EXPECT_CALL(*Output(kOutputIndex), OnProcess(IsStreamInfo(kStreamIndex)));
|
|
||||||
|
|
||||||
// Segment One
|
|
||||||
EXPECT_CALL(
|
|
||||||
*Output(kOutputIndex),
|
|
||||||
OnProcess(IsTextSample(kId[0], kStartTime, kStartTime + kSampleDuration,
|
|
||||||
kNoSettings, kPayload[0])));
|
|
||||||
EXPECT_CALL(
|
|
||||||
*Output(kOutputIndex),
|
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kStartTime, kSegmentDuration,
|
|
||||||
!kSubSegment, !kEncrypted)));
|
|
||||||
|
|
||||||
// Segment Two
|
|
||||||
EXPECT_CALL(
|
|
||||||
*Output(kOutputIndex),
|
|
||||||
OnProcess(IsTextSample(kId[0], kStartTime, kStartTime + kSampleDuration,
|
|
||||||
kNoSettings, kPayload[0])));
|
|
||||||
EXPECT_CALL(
|
|
||||||
*Output(kOutputIndex),
|
|
||||||
OnProcess(IsSegmentInfo(kStreamIndex, kStartTime + kSegmentDuration,
|
|
||||||
kSegmentDuration, !kSubSegment, !kEncrypted)));
|
|
||||||
|
|
||||||
EXPECT_CALL(*Output(kOutputIndex), OnFlush(kStreamIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_OK(Input(kInputIndex)
|
|
||||||
->Dispatch(StreamData::FromStreamInfo(kStreamIndex,
|
|
||||||
GetTextStreamInfo())));
|
|
||||||
ASSERT_OK(Input(kInputIndex)
|
|
||||||
->Dispatch(StreamData::FromTextSample(
|
|
||||||
kStreamIndex,
|
|
||||||
GetTextSample(kId[0], kStartTime,
|
|
||||||
kStartTime + kSampleDuration, kPayload[0]))));
|
|
||||||
ASSERT_OK(Input(kInputIndex)->FlushAllDownstreams());
|
|
||||||
}
|
|
||||||
|
|
||||||
class TextChunkerOrderTest : public MediaHandlerTestBase {};
|
|
||||||
|
|
||||||
TEST_F(TextChunkerOrderTest, PreservesOrder) {
|
|
||||||
const size_t kInputs = 1;
|
|
||||||
const size_t kOutputs = 1;
|
|
||||||
|
|
||||||
const size_t kInput = 0;
|
|
||||||
const size_t kOutput = 0;
|
|
||||||
|
|
||||||
const int64_t kDuration = 10000;
|
|
||||||
const int64_t kSegmentStart1 = 0;
|
|
||||||
const int64_t kSegmentStart2 = kDuration;
|
|
||||||
|
|
||||||
ASSERT_OK(SetUpAndInitializeGraph(std::make_shared<TextChunker>(kDuration),
|
|
||||||
kInputs, kOutputs));
|
|
||||||
|
|
||||||
{
|
|
||||||
testing::InSequence s;
|
|
||||||
|
|
||||||
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kInput)));
|
|
||||||
|
|
||||||
// Segment One
|
|
||||||
EXPECT_CALL(*Output(kOutput),
|
|
||||||
OnProcess(IsTextSample("1", 5000u, 8500u, "",
|
|
||||||
"WebVtt testing Line 1 (5.0 - 8.5)")));
|
|
||||||
EXPECT_CALL(*Output(kOutput),
|
|
||||||
OnProcess(IsTextSample("2", 5000u, 8500u, "",
|
|
||||||
"WebVtt testing Line 2 (5.0 - 8.5)")));
|
|
||||||
EXPECT_CALL(*Output(kOutput),
|
|
||||||
OnProcess(IsTextSample("3", 5000u, 12500u, "",
|
|
||||||
"WebVtt testing (5.0 - 12.5)")));
|
|
||||||
EXPECT_CALL(*Output(kOutput), OnProcess(IsSegmentInfo(
|
|
||||||
kInput, kSegmentStart1, kSegmentDuration,
|
|
||||||
!kSubSegment, !kEncrypted)));
|
|
||||||
|
|
||||||
// Segment Two
|
|
||||||
EXPECT_CALL(*Output(kOutput),
|
|
||||||
OnProcess(IsTextSample("3", 5000u, 12500u, "",
|
|
||||||
"WebVtt testing (5.0 - 12.5)")));
|
|
||||||
EXPECT_CALL(*Output(kOutput), OnProcess(IsSegmentInfo(
|
|
||||||
kInput, kSegmentStart2, kSegmentDuration,
|
|
||||||
!kSubSegment, !kEncrypted)));
|
|
||||||
|
|
||||||
EXPECT_CALL(*Output(kOutput), OnFlush(kInput));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(
|
ASSERT_OK(Input(kInput)->Dispatch(
|
||||||
StreamData::FromStreamInfo(0, GetTextStreamInfo())));
|
StreamData::FromStreamInfo(kStreamIndex, GetTextStreamInfo())));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
kOutput,
|
kStreamIndex,
|
||||||
GetTextSample("1", 5000, 8500, "WebVtt testing Line 1 (5.0 - 8.5)"))));
|
GetTextSample(kNoId, kSample0Start, kSample0End, kNoPayload))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
kOutput,
|
kStreamIndex,
|
||||||
GetTextSample("2", 5000, 8500, "WebVtt testing Line 2 (5.0 - 8.5)"))));
|
GetTextSample(kNoId, kSample1Start, kSample1End, kNoPayload))));
|
||||||
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
||||||
kOutput,
|
}
|
||||||
GetTextSample("3", 5000, 12500, "WebVtt testing (5.0 - 12.5)"))));
|
|
||||||
|
|
||||||
ASSERT_OK(Input(kInputIndex)->FlushAllDownstreams());
|
// S0 S1 S2
|
||||||
|
// | | |
|
||||||
|
// | [---A---] |
|
||||||
|
// | | |
|
||||||
|
TEST_F(TextChunkerTest, SampleCrossesSegments) {
|
||||||
|
const int64_t kSegmentDuration = 2 * kTick;
|
||||||
|
const int64_t kSegment0Start = 0;
|
||||||
|
const int64_t kSegment1Start = kSegment0Start + kSegmentDuration;
|
||||||
|
|
||||||
|
const int64_t kSampleStart = kTick;
|
||||||
|
const int64_t kSampleDuration = 2 * kTick;
|
||||||
|
const int64_t kSampleEnd = kSampleStart + kSampleDuration;
|
||||||
|
|
||||||
|
Init(kSegmentDuration);
|
||||||
|
|
||||||
|
{
|
||||||
|
testing::InSequence s;
|
||||||
|
|
||||||
|
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
|
||||||
|
|
||||||
|
// Segment One
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSampleStart, kSampleEnd,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*Output(kOutput),
|
||||||
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start, kSegmentDuration,
|
||||||
|
!kSubSegment, !kEncrypted)));
|
||||||
|
|
||||||
|
// Segment Two
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSampleStart, kSampleEnd,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*Output(kOutput),
|
||||||
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start, kSegmentDuration,
|
||||||
|
!kSubSegment, !kEncrypted)));
|
||||||
|
|
||||||
|
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_OK(Input(kInput)->Dispatch(
|
||||||
|
StreamData::FromStreamInfo(kStreamIndex, GetTextStreamInfo())));
|
||||||
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
|
kStreamIndex,
|
||||||
|
GetTextSample(kNoId, kSampleStart, kSampleEnd, kNoPayload))));
|
||||||
|
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
||||||
|
}
|
||||||
|
|
||||||
|
// S0 S1 S2 S3
|
||||||
|
// | | | |
|
||||||
|
// | [-A----|----] | |
|
||||||
|
// | [-B----|----] | |
|
||||||
|
// | [-C----|----------|----] |
|
||||||
|
// | | | |
|
||||||
|
TEST_F(TextChunkerTest, PreservesOrder) {
|
||||||
|
const int64_t kSegmentDuration = 2 * kTick;
|
||||||
|
|
||||||
|
const int64_t kSegment0Start = 0;
|
||||||
|
const int64_t kSegment1Start = kSegment0Start + kSegmentDuration;
|
||||||
|
const int64_t kSegment2Start = kSegment1Start + kSegmentDuration;
|
||||||
|
|
||||||
|
const int64_t kSample0Start = kTick;
|
||||||
|
const int64_t kSample0Duration = 2 * kTick;
|
||||||
|
const int64_t kSample0End = kSample0Start + kSample0Duration;
|
||||||
|
|
||||||
|
const int64_t kSample1Start = kTick;
|
||||||
|
const int64_t kSample1Duration = 2 * kTick;
|
||||||
|
const int64_t kSample1End = kSample1Start + kSample1Duration;
|
||||||
|
|
||||||
|
const int64_t kSample2Start = kTick;
|
||||||
|
const int64_t kSample2Duration = 4 * kTick;
|
||||||
|
const int64_t kSample2End = kSample2Start + kSample2Duration;
|
||||||
|
|
||||||
|
const char* kSample0Id = "sample 0";
|
||||||
|
const char* kSample1Id = "sample 1";
|
||||||
|
const char* kSample2Id = "sample 2";
|
||||||
|
|
||||||
|
Init(kSegmentDuration);
|
||||||
|
|
||||||
|
{
|
||||||
|
testing::InSequence s;
|
||||||
|
|
||||||
|
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
|
||||||
|
|
||||||
|
// Segment One
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kSample0Id, kSample0Start, kSample0End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kSample1Id, kSample1Start, kSample1End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kSample2Id, kSample2Start, kSample2End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*Output(kOutput),
|
||||||
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start, kSegmentDuration,
|
||||||
|
!kSubSegment, !kEncrypted)));
|
||||||
|
|
||||||
|
// Segment Two
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kSample0Id, kSample0Start, kSample0End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kSample1Id, kSample1Start, kSample1End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kSample2Id, kSample2Start, kSample2End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*Output(kOutput),
|
||||||
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start, kSegmentDuration,
|
||||||
|
!kSubSegment, !kEncrypted)));
|
||||||
|
|
||||||
|
// Segment Two
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kSample2Id, kSample2Start, kSample2End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*Output(kOutput),
|
||||||
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start, kSegmentDuration,
|
||||||
|
!kSubSegment, !kEncrypted)));
|
||||||
|
|
||||||
|
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_OK(Input(kInput)->Dispatch(
|
||||||
|
StreamData::FromStreamInfo(kStreamIndex, GetTextStreamInfo())));
|
||||||
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
|
kStreamIndex,
|
||||||
|
GetTextSample(kSample0Id, kSample0Start, kSample0End, kNoPayload))));
|
||||||
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
|
kStreamIndex,
|
||||||
|
GetTextSample(kSample1Id, kSample1Start, kSample1End, kNoPayload))));
|
||||||
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
|
kStreamIndex,
|
||||||
|
GetTextSample(kSample2Id, kSample2Start, kSample2End, kNoPayload))));
|
||||||
|
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
||||||
|
}
|
||||||
|
|
||||||
|
// S0 S1 S2 S3 S4 S5
|
||||||
|
// | | | | | |
|
||||||
|
// | [--|-----|--A--|-----|--] |
|
||||||
|
// | | [--|--B--|--] | |
|
||||||
|
// | | | | | |
|
||||||
|
TEST_F(TextChunkerTest, NestedSamples) {
|
||||||
|
const int64_t kSegmentDuration = 2 * kTick;
|
||||||
|
|
||||||
|
const int64_t kSample0Start = 1 * kTick;
|
||||||
|
const int64_t kSample0Duration = 8 * kTick;
|
||||||
|
const int64_t kSample0End = kSample0Start + kSample0Duration;
|
||||||
|
|
||||||
|
const int64_t kSample1Start = 3 * kTick;
|
||||||
|
const int64_t kSample1Duration = 4 * kTick;
|
||||||
|
const int64_t kSample1End = kSample1Start + kSample1Duration;
|
||||||
|
|
||||||
|
const int64_t kSegment0Start = 0;
|
||||||
|
const int64_t kSegment1Start = kSegment0Start + kSegmentDuration;
|
||||||
|
const int64_t kSegment2Start = kSegment1Start + kSegmentDuration;
|
||||||
|
const int64_t kSegment3Start = kSegment2Start + kSegmentDuration;
|
||||||
|
const int64_t kSegment4Start = kSegment3Start + kSegmentDuration;
|
||||||
|
|
||||||
|
Init(kSegmentDuration);
|
||||||
|
|
||||||
|
{
|
||||||
|
testing::InSequence s;
|
||||||
|
|
||||||
|
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
|
||||||
|
|
||||||
|
// Segment 0
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*Output(kOutput),
|
||||||
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start, kSegmentDuration,
|
||||||
|
!kSubSegment, !kEncrypted)));
|
||||||
|
|
||||||
|
// Segment 1
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSample1Start, kSample1End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*Output(kOutput),
|
||||||
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start, kSegmentDuration,
|
||||||
|
!kSubSegment, !kEncrypted)));
|
||||||
|
|
||||||
|
// Segment 2
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSample1Start, kSample1End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*Output(kOutput),
|
||||||
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start, kSegmentDuration,
|
||||||
|
!kSubSegment, !kEncrypted)));
|
||||||
|
|
||||||
|
// Segment 3
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSample1Start, kSample1End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*Output(kOutput),
|
||||||
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment3Start, kSegmentDuration,
|
||||||
|
!kSubSegment, !kEncrypted)));
|
||||||
|
|
||||||
|
// Segment 4
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*Output(kOutput),
|
||||||
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment4Start, kSegmentDuration,
|
||||||
|
!kSubSegment, !kEncrypted)));
|
||||||
|
|
||||||
|
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_OK(Input(kInput)->Dispatch(
|
||||||
|
StreamData::FromStreamInfo(kStreamIndex, GetTextStreamInfo())));
|
||||||
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
|
kStreamIndex,
|
||||||
|
GetTextSample(kNoId, kSample0Start, kSample0End, kNoPayload))));
|
||||||
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
|
kStreamIndex,
|
||||||
|
GetTextSample(kNoId, kSample1Start, kSample1End, kNoPayload))));
|
||||||
|
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
||||||
|
}
|
||||||
|
|
||||||
|
// S0 S1 S2 S3
|
||||||
|
// | | | |
|
||||||
|
// | [------A--------]| |
|
||||||
|
// | | |[--B--] |
|
||||||
|
// | | | |
|
||||||
|
TEST_F(TextChunkerTest, SecondSampleStartsAfterMultiSegmentSampleEnds) {
|
||||||
|
const int64_t kSegmentDuration = 2 * kTick;
|
||||||
|
const int64_t kSegment0Start = 0;
|
||||||
|
const int64_t kSegment1Start = kSegment0Start + kSegmentDuration;
|
||||||
|
const int64_t kSegment2Start = kSegment1Start + kSegmentDuration;
|
||||||
|
|
||||||
|
const int64_t kSample0Start = kTick;
|
||||||
|
const int64_t kSample0Duration = 3 * kTick;
|
||||||
|
const int64_t kSample0End = kSample0Start + kSample0Duration;
|
||||||
|
|
||||||
|
const int64_t kSample1Start = 4 * kTick;
|
||||||
|
const int64_t kSample1Duration = kTick;
|
||||||
|
const int64_t kSample1End = kSample1Start + kSample1Duration;
|
||||||
|
|
||||||
|
Init(kSegmentDuration);
|
||||||
|
|
||||||
|
{
|
||||||
|
testing::InSequence s;
|
||||||
|
|
||||||
|
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
|
||||||
|
|
||||||
|
// Segment One
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*Output(kOutput),
|
||||||
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start, kSegmentDuration,
|
||||||
|
!kSubSegment, !kEncrypted)));
|
||||||
|
|
||||||
|
// Segment Two
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSample0Start, kSample0End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*Output(kOutput),
|
||||||
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start, kSegmentDuration,
|
||||||
|
!kSubSegment, !kEncrypted)));
|
||||||
|
|
||||||
|
// Segment Three
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSample1Start, kSample1End,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*Output(kOutput),
|
||||||
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start, kSegmentDuration,
|
||||||
|
!kSubSegment, !kEncrypted)));
|
||||||
|
|
||||||
|
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_OK(Input(kInput)->Dispatch(
|
||||||
|
StreamData::FromStreamInfo(kStreamIndex, GetTextStreamInfo())));
|
||||||
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
|
kStreamIndex,
|
||||||
|
GetTextSample(kNoId, kSample0Start, kSample0End, kNoPayload))));
|
||||||
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
|
kStreamIndex,
|
||||||
|
GetTextSample(kNoId, kSample1Start, kSample1End, kNoPayload))));
|
||||||
|
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
||||||
|
}
|
||||||
|
|
||||||
|
// S0 C0 C1 S1 * *
|
||||||
|
// s0 s1 s2 * * s3
|
||||||
|
// | | | |
|
||||||
|
// | [---|-----A----|---] |
|
||||||
|
// | | | |
|
||||||
|
// 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.
|
||||||
|
TEST_F(TextChunkerTest, SampleSpanningMultipleCues) {
|
||||||
|
const int64_t kSegmentDuration = 8 * kTick;
|
||||||
|
|
||||||
|
const int64_t kSampleStart = kTick;
|
||||||
|
const int64_t kSampleDuration = 4 * kTick;
|
||||||
|
const int64_t kSampleEnd = kSampleStart + kSampleDuration;
|
||||||
|
|
||||||
|
const int64_t kCue0Time = 2 * kTick;
|
||||||
|
const double kCue0TimeInSeconds = kCue0Time / 1000.0;
|
||||||
|
const int64_t kCue1Time = 4 * kTick;
|
||||||
|
const double kCue1TimeInSeconds = kCue1Time / 1000.0;
|
||||||
|
|
||||||
|
const int64_t kSegment0Start = 0;
|
||||||
|
const int64_t kSegment0Duration = 2 * kTick;
|
||||||
|
const int64_t kSegment1Start = kSegment0Start + kSegment0Duration;
|
||||||
|
const int64_t kSegment1Duration = 2 * kTick;
|
||||||
|
const int64_t kSegment2Start = kSegment1Start + kSegment1Duration;
|
||||||
|
const int64_t kSegment2Duration = 8 * kTick;
|
||||||
|
|
||||||
|
Init(kSegmentDuration);
|
||||||
|
|
||||||
|
{
|
||||||
|
testing::InSequence s;
|
||||||
|
|
||||||
|
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
|
||||||
|
|
||||||
|
// Segment 0 and Cue 0
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSampleStart, kSampleEnd,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*Output(kOutput),
|
||||||
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start, kSegment0Duration,
|
||||||
|
!kSubSegment, !kEncrypted)));
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsCueEvent(kStreamIndex, kCue0TimeInSeconds)));
|
||||||
|
|
||||||
|
// Segment 1 and Cue 1
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSampleStart, kSampleEnd,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*Output(kOutput),
|
||||||
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start, kSegment1Duration,
|
||||||
|
!kSubSegment, !kEncrypted)));
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsCueEvent(kStreamIndex, kCue1TimeInSeconds)));
|
||||||
|
|
||||||
|
// Segment 2
|
||||||
|
EXPECT_CALL(*Output(kOutput),
|
||||||
|
OnProcess(IsTextSample(kNoId, kSampleStart, kSampleEnd,
|
||||||
|
kNoSettings, kNoPayload)));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*Output(kOutput),
|
||||||
|
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start, kSegment2Duration,
|
||||||
|
!kSubSegment, !kEncrypted)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_OK(Input(kInput)->Dispatch(
|
||||||
|
StreamData::FromStreamInfo(kStreamIndex, GetTextStreamInfo())));
|
||||||
|
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
|
||||||
|
kStreamIndex,
|
||||||
|
GetTextSample(kNoId, kSampleStart, kSampleEnd, kNoPayload))));
|
||||||
|
ASSERT_OK(Input(kInput)->Dispatch(
|
||||||
|
StreamData::FromCueEvent(kStreamIndex, GetCueEvent(kCue0TimeInSeconds))));
|
||||||
|
ASSERT_OK(Input(kInput)->Dispatch(
|
||||||
|
StreamData::FromCueEvent(kStreamIndex, GetCueEvent(kCue1TimeInSeconds))));
|
||||||
|
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
Loading…
Reference in New Issue