shaka-packager/packager/media/chunking/text_chunker_unittest.cc

638 lines
23 KiB
C++
Raw Normal View History

// Copyright 2017 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "packager/media/base/media_handler_test_base.h"
#include "packager/media/chunking/text_chunker.h"
#include "packager/status_test_util.h"
namespace shaka {
namespace media {
namespace {
const uint64_t kStreamIndex = 0;
const uint64_t kMsTimeScale = 1000u;
const size_t kInputs = 1;
const size_t kOutputs = 1;
const size_t kInput = 0;
const size_t kOutput = 0;
const bool kEncrypted = true;
const bool kSubSegment = true;
const uint64_t kTimescaleMs = 1000;
const char* kNoId = "";
const char* kNoSettings = "";
const char* kNoPayload = "";
} // namespace
class TextChunkerTest : public MediaHandlerTestBase {
protected:
void Init(double segment_duration) {
ASSERT_OK(SetUpAndInitializeGraph(
std::make_shared<TextChunker>(segment_duration), kInputs, kOutputs));
}
};
// Verify that when a sample elapses a full segment, that it only appears
// in the one segment.
//
// Segment Duration = 100 MS
//
// TIME (ms):0 5 1
// 0 0
// 0
// SAMPLES :[-----A-----]
// SEGMENTS : ^
//
TEST_F(TextChunkerTest, SampleEndingOnSegmentStart) {
const double kSegmentDurationSec = 0.1;
const int64_t kSegmentDurationMs = 100;
const int64_t kSegment0Start = 0;
const int64_t kSampleAStart = 0;
const int64_t kSampleAEnd = 100;
Init(kSegmentDurationSec);
{
testing::InSequence s;
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
}
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromStreamInfo(
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
kStreamIndex,
GetTextSample(kNoId, kSampleAStart, kSampleAEnd, kNoPayload))));
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
}
// Verify that samples only appear in the correct segments when they only exist
// in one segment.
//
// 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) {
const double kSegmentDurationSec = 0.1;
const int64_t kSegmentDurationMs = 100;
const int64_t kSegment0Start = 0;
const int64_t kSegment1Start = 100;
const int64_t kSampleAStart = 25;
const int64_t kSampleAEnd = 75;
const int64_t kSampleBStart = 125;
const int64_t kSampleBEnd = 175;
Init(kSegmentDurationSec);
{
testing::InSequence s;
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
// Segment One
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
// Segment Two
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleBStart, kSampleBEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
}
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromStreamInfo(
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
kStreamIndex,
GetTextSample(kNoId, kSampleAStart, kSampleAEnd, kNoPayload))));
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
kStreamIndex,
GetTextSample(kNoId, kSampleBStart, kSampleBEnd, kNoPayload))));
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
}
// Verify that a segment will get outputted even if there are no samples
// overlapping with it.
//
// 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) {
const double kSegmentDurationSec = 0.1;
const int64_t kSegmentDurationMs = 100;
const int64_t kSegment0Start = 0;
const int64_t kSegment1Start = 100;
const int64_t kSegment2Start = 200;
const int64_t kSampleAStart = 25;
const int64_t kSampleAEnd = 75;
const int64_t kSampleBStart = 225;
const int64_t kSampleBEnd = 275;
Init(kSegmentDurationSec);
{
testing::InSequence s;
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
// Segment One
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
// Segment Two (empty segment)
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
// Segment Three
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleBStart, kSampleBEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
}
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromStreamInfo(
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
kStreamIndex,
GetTextSample(kNoId, kSampleAStart, kSampleAEnd, kNoPayload))));
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
kStreamIndex,
GetTextSample(kNoId, kSampleBStart, kSampleBEnd, kNoPayload))));
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
}
// Verify that samples that overlap multiple samples get dispatch in all
// segments.
//
// Segment Duration = 100 MS
//
// TIME (ms):0 5 1 1
// 0 0 5
// 0 0
// SAMPLES : [-----A-----]
// SEGMENTS : ^
//
TEST_F(TextChunkerTest, SampleCrossesSegments) {
const double kSegmentDurationSec = 0.1;
const int64_t kSegmentDurationMs = 100;
const int64_t kSegment0Start = 0;
const int64_t kSegment1Start = 100;
const int64_t kSampleAStart = 50;
const int64_t kSampleAEnd = 150;
Init(kSegmentDurationSec);
{
testing::InSequence s;
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
// Segment One
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
// Segment Two
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
}
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromStreamInfo(
kStreamIndex, GetTextStreamInfo(kTimescaleMs))));
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
kStreamIndex,
GetTextSample(kNoId, kSampleAStart, kSampleAEnd, kNoPayload))));
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
}
// Verify that samples that overlap multiple samples get dispatch in all
// segments, even if different samples elapse different number of segments.
//
// 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-----]
// [-----------C-----------]
// SEGMENTS : ^ ^ ^
//
TEST_F(TextChunkerTest, PreservesOrder) {
const double kSegmentDurationSec = 0.1;
const int64_t kSegmentDurationMs = 100;
const int64_t kSegment0Start = 0;
const int64_t kSegment1Start = 100;
const int64_t kSegment2Start = 200;
const int64_t kSampleAStart = 50;
const int64_t kSampleAEnd = 150;
const int64_t kSampleBStart = 50;
const int64_t kSampleBEnd = 150;
const int64_t kSampleCStart = 50;
const int64_t kSampleCEnd = 250;
const char* kSampleAId = "sample 0";
const char* kSampleBId = "sample 1";
const char* kSampleCId = "sample 2";
Init(kSegmentDurationSec);
{
testing::InSequence s;
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
// Segment One
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kSampleAId, kSampleAStart, kSampleAEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kSampleBId, kSampleBStart, kSampleBEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kSampleCId, kSampleCStart, kSampleCEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
// Segment Two
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kSampleAId, kSampleAStart, kSampleAEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kSampleBId, kSampleBStart, kSampleBEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kSampleCId, kSampleCStart, kSampleCEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
// Segment Two
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kSampleCId, kSampleCStart, kSampleCEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
}
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromStreamInfo(
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
kStreamIndex,
GetTextSample(kSampleAId, kSampleAStart, kSampleAEnd, kNoPayload))));
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
kStreamIndex,
GetTextSample(kSampleBId, kSampleBStart, kSampleBEnd, kNoPayload))));
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
kStreamIndex,
GetTextSample(kSampleCId, kSampleCStart, kSampleCEnd, kNoPayload))));
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
}
// Check that when samples overlap/contain other samples, that they still
// get outputted in the correct segments.
//
// 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) {
const double kSegmentDurationSec = 0.05;
const int64_t kSegmentDurationMs = 50;
const int64_t kSampleAStart = 25;
const int64_t kSampleAEnd = 225;
const int64_t kSampleBStart = 75;
const int64_t kSampleBEnd = 175;
const int64_t kSegment0Start = 0;
const int64_t kSegment1Start = 50;
const int64_t kSegment2Start = 100;
const int64_t kSegment3Start = 150;
const int64_t kSegment4Start = 200;
Init(kSegmentDurationSec);
{
testing::InSequence s;
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
// Segment 0
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
// Segment 1
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleBStart, kSampleBEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
// Segment 2
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleBStart, kSampleBEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
// Segment 3
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleBStart, kSampleBEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment3Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
// Segment 4
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment4Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
}
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromStreamInfo(
kStreamIndex, GetTextStreamInfo(kMsTimeScale))));
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
kStreamIndex,
GetTextSample(kNoId, kSampleAStart, kSampleAEnd, kNoPayload))));
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
kStreamIndex,
GetTextSample(kNoId, kSampleBStart, kSampleBEnd, kNoPayload))));
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
}
// Make sure that a sample that extends multiple segments is dropped when
// it no longer overlaps with a later segment.
//
// 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) {
const double kSegmentDurationSec = 0.1;
const int64_t kSegmentDurationMs = 100;
const int64_t kSegment0Start = 0;
const int64_t kSegment1Start = 100;
const int64_t kSegment2Start = 200;
const int64_t kSampleAStart = 50;
const int64_t kSampleAEnd = 200;
const int64_t kSampleBStart = 200;
const int64_t kSampleBEnd = 250;
Init(kSegmentDurationSec);
{
testing::InSequence s;
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
// Segment One
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
// Segment Two
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
// Segment Three
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleBStart, kSampleBEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
EXPECT_CALL(*Output(kOutput), OnFlush(kStreamIndex));
}
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromStreamInfo(
kStreamIndex, GetTextStreamInfo(kTimescaleMs))));
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
kStreamIndex,
GetTextSample(kNoId, kSampleAStart, kSampleAEnd, kNoPayload))));
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
kStreamIndex,
GetTextSample(kNoId, kSampleBStart, kSampleBEnd, kNoPayload))));
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
}
// Check that segments will be injected when a cue event comes down the
// pipeline and that the segment duration will get reset after the cues
// are dispatched.
//
// Segment Duration = 300 MS
//
// 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) {
const double kSegmentDurationSec = 0.3;
const int64_t kSegmentDurationMs = 300;
const int64_t kSampleAStart = 50;
const int64_t kSampleAEnd = 250;
const double kC0 = 0.1;
const double kC1 = 0.2;
const int64_t kSegment0Start = 0;
const int64_t kSegment1Start = 100;
const int64_t kSegment2Start = 200;
;
const double kSegment0StartLength = 100;
const double kSegment1StartLength = 100;
Init(kSegmentDurationSec);
{
testing::InSequence s;
EXPECT_CALL(*Output(kOutput), OnProcess(IsStreamInfo(kStreamIndex)));
// Segment 0 and Cue 0
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment0Start,
kSegment0StartLength, !kSubSegment,
!kEncrypted)));
EXPECT_CALL(*Output(kOutput), OnProcess(IsCueEvent(kStreamIndex, kC0)));
// Segment 1 and Cue 1
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment1Start,
kSegment1StartLength, !kSubSegment,
!kEncrypted)));
EXPECT_CALL(*Output(kOutput), OnProcess(IsCueEvent(kStreamIndex, kC1)));
// Segment 2
EXPECT_CALL(*Output(kOutput),
OnProcess(IsTextSample(kNoId, kSampleAStart, kSampleAEnd,
kNoSettings, kNoPayload)));
EXPECT_CALL(*Output(kOutput),
OnProcess(IsSegmentInfo(kStreamIndex, kSegment2Start,
kSegmentDurationMs, !kSubSegment,
!kEncrypted)));
}
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromStreamInfo(
kStreamIndex, GetTextStreamInfo(kTimescaleMs))));
ASSERT_OK(Input(kInput)->Dispatch(StreamData::FromTextSample(
kStreamIndex,
GetTextSample(kNoId, kSampleAStart, kSampleAEnd, kNoPayload))));
ASSERT_OK(Input(kInput)->Dispatch(
StreamData::FromCueEvent(kStreamIndex, GetCueEvent(kC0))));
ASSERT_OK(Input(kInput)->Dispatch(
StreamData::FromCueEvent(kStreamIndex, GetCueEvent(kC1))));
ASSERT_OK(Input(kInput)->FlushAllDownstreams());
}
} // namespace media
} // namespace shaka