Output Empty MP4 WebVtt Cues
When a gap is found in the text stream, the WebVtt to Mp4 converter will now output the special empty vtt cue. Change-Id: I8be88c6b7589aa120a2215e1e4b8e98031fe326d Closes: #324
This commit is contained in:
parent
8257eea804
commit
f35410deb1
|
@ -129,14 +129,10 @@ Status WebVttToMp4Handler::ProcessUpToTime(uint64_t cutoff_time) {
|
|||
// Get the time range for which the current active state is valid.
|
||||
const uint64_t previous_change = next_change_;
|
||||
next_change_ = actions_.top()->time();
|
||||
// The only time that |previous_change| and |next_change_| should ever break
|
||||
// the rule |next_change_ > previous_change| is at the start where
|
||||
// |previous_change| and |next_change_| are both zero.
|
||||
DCHECK((previous_change == 0 && next_change_ == 0) ||
|
||||
next_change_ > previous_change);
|
||||
|
||||
// Send out the active group. If there is nothing in the active group, then
|
||||
// an empty cue is sent.
|
||||
if (next_change_ > previous_change) {
|
||||
// Send out the active group. If there is nothing in the active group,
|
||||
// then an empty cue is sent.
|
||||
Status status =
|
||||
active_.size()
|
||||
? MergeAndSendSamples(active_, previous_change, next_change_)
|
||||
|
@ -145,6 +141,13 @@ Status WebVttToMp4Handler::ProcessUpToTime(uint64_t cutoff_time) {
|
|||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
} else {
|
||||
// The only time that |previous_change| and |next_change_| should ever
|
||||
// break the rule |next_change_ > previous_change| is at the start where
|
||||
// |previous_change| and |next_change_| are both zero.
|
||||
DCHECK_EQ(previous_change, 0u);
|
||||
DCHECK_EQ(next_change_, 0u);
|
||||
}
|
||||
|
||||
// STAGE 2: Move to the next state.
|
||||
while (actions_.size() && actions_.top()->time() == next_change_) {
|
||||
|
@ -180,8 +183,19 @@ Status WebVttToMp4Handler::MergeAndSendSamples(
|
|||
|
||||
Status WebVttToMp4Handler::SendEmptySample(uint64_t start_time,
|
||||
uint64_t end_time) {
|
||||
// TODO(vaage): Dispatch an empty vtt sample.
|
||||
return Status::OK;
|
||||
DCHECK_GT(end_time, start_time);
|
||||
|
||||
box_writer_.Clear();
|
||||
|
||||
mp4::VTTEmptyCueBox box;
|
||||
box.Write(&box_writer_);
|
||||
|
||||
std::shared_ptr<MediaSample> sample =
|
||||
MediaSample::CopyFrom(box_writer_.Buffer(), box_writer_.Size(), true);
|
||||
sample->set_pts(start_time);
|
||||
sample->set_dts(start_time);
|
||||
sample->set_duration(end_time - start_time);
|
||||
return DispatchMediaSample(kTrackId, std::move(sample));
|
||||
}
|
||||
|
||||
uint64_t WebVttToMp4Handler::NextActionId() {
|
||||
|
|
|
@ -63,32 +63,104 @@ class WebVttToMp4HandlerTest : public MediaHandlerTestBase {
|
|||
std::shared_ptr<TestableWebVttToMp4Handler> mp4_handler_;
|
||||
};
|
||||
|
||||
// Verify the cues are grouped correctly when the cues do not overlap at all.
|
||||
//
|
||||
// [----A---] [---B---]
|
||||
TEST_F(WebVttToMp4HandlerTest, NoOverlap) {
|
||||
const int64_t kStart[] = {0, 1100};
|
||||
const int64_t kEnd[] = {1000, 2100};
|
||||
// Verify that when the stream starts at a non-zero value, the gap at the
|
||||
// start will be filled.
|
||||
// | [----A----]
|
||||
TEST_F(WebVttToMp4HandlerTest, NonZeroStartTime) {
|
||||
const int64_t kGapStart = 0;
|
||||
const int64_t kGapEnd = 100;
|
||||
const int64_t kGapDuration = kGapEnd - kGapStart;
|
||||
|
||||
const char* kSampleId = kId[0];
|
||||
const char* kSamplePayload = kPayload[0];
|
||||
const int64_t kSampleStart = kGapEnd;
|
||||
const int64_t kSampleDuration = 500;
|
||||
const int64_t kSampleEnd = kSampleStart + kSampleDuration;
|
||||
|
||||
{
|
||||
testing::InSequence s;
|
||||
|
||||
for (size_t i = kA; i <= kB; i++) {
|
||||
EXPECT_CALL(*mp4_handler_, OnWriteCue(kId[i], kNoSettings, kPayload[i]));
|
||||
// Empty Cue to fill gap
|
||||
EXPECT_CALL(*Output(kOutputIndex),
|
||||
OnProcess(IsMediaSample(kStreamIndex, kStart[i],
|
||||
kEnd[i] - kStart[i], !kEncrypted)));
|
||||
}
|
||||
OnProcess(IsMediaSample(kStreamIndex, kGapStart, kGapDuration,
|
||||
!kEncrypted)));
|
||||
|
||||
// Sample
|
||||
EXPECT_CALL(*mp4_handler_,
|
||||
OnWriteCue(kSampleId, kNoSettings, kSamplePayload));
|
||||
EXPECT_CALL(*Output(kOutputIndex),
|
||||
OnProcess(IsMediaSample(kStreamIndex, kSampleStart,
|
||||
kSampleDuration, !kEncrypted)));
|
||||
|
||||
EXPECT_CALL(*Output(kOutputIndex), OnFlush(kStreamIndex));
|
||||
}
|
||||
|
||||
for (size_t i = kA; i <= kB; i++) {
|
||||
ASSERT_OK(Input(kInputIndex)
|
||||
->Dispatch(StreamData::FromTextSample(
|
||||
kStreamIndex,
|
||||
GetTextSample(kId[i], kStart[i], kEnd[i], kPayload[i]))));
|
||||
kStreamIndex, GetTextSample(kSampleId, kSampleStart,
|
||||
kSampleEnd, kSamplePayload))));
|
||||
|
||||
ASSERT_OK(Input(kInputIndex)->FlushAllDownstreams());
|
||||
}
|
||||
|
||||
// Verify the cues are grouped correctly when the cues do not overlap at all.
|
||||
// An empty cue should be inserted between the two as there is a gap.
|
||||
//
|
||||
// [----A---] [---B---]
|
||||
TEST_F(WebVttToMp4HandlerTest, NoOverlap) {
|
||||
const int64_t kDuration = 1000;
|
||||
|
||||
const char* kSample1Id = kId[0];
|
||||
const char* kSample1Payload = kPayload[0];
|
||||
const int64_t kSample1Start = 0;
|
||||
const int64_t kSample1End = kSample1Start + kDuration;
|
||||
|
||||
// Make sample 2 be just a little after sample 1.
|
||||
const char* kSample2Id = kId[1];
|
||||
const char* kSample2Payload = kPayload[1];
|
||||
const int64_t kSample2Start = kSample1End + 100;
|
||||
const int64_t kSample2End = kSample2Start + kDuration;
|
||||
|
||||
const int64_t kGapStart = kSample1End;
|
||||
const int64_t kGapDuration = kSample2Start - kSample1End;
|
||||
|
||||
{
|
||||
testing::InSequence s;
|
||||
|
||||
// Sample 1
|
||||
EXPECT_CALL(*mp4_handler_,
|
||||
OnWriteCue(kSample1Id, kNoSettings, kSample1Payload));
|
||||
EXPECT_CALL(*Output(kOutputIndex),
|
||||
OnProcess(IsMediaSample(kStreamIndex, kSample1Start, kDuration,
|
||||
!kEncrypted)));
|
||||
|
||||
// Empty Cue to fill gap
|
||||
EXPECT_CALL(*Output(kOutputIndex),
|
||||
OnProcess(IsMediaSample(kStreamIndex, kGapStart, kGapDuration,
|
||||
!kEncrypted)));
|
||||
|
||||
// Sample 2
|
||||
EXPECT_CALL(*mp4_handler_,
|
||||
OnWriteCue(kSample2Id, kNoSettings, kSample2Payload));
|
||||
EXPECT_CALL(*Output(kOutputIndex),
|
||||
OnProcess(IsMediaSample(kStreamIndex, kSample2Start, kDuration,
|
||||
!kEncrypted)));
|
||||
|
||||
EXPECT_CALL(*Output(kOutputIndex), OnFlush(kStreamIndex));
|
||||
}
|
||||
|
||||
ASSERT_OK(
|
||||
Input(kInputIndex)
|
||||
->Dispatch(StreamData::FromTextSample(
|
||||
kStreamIndex, GetTextSample(kSample1Id, kSample1Start,
|
||||
kSample1End, kSample1Payload))));
|
||||
|
||||
ASSERT_OK(
|
||||
Input(kInputIndex)
|
||||
->Dispatch(StreamData::FromTextSample(
|
||||
kStreamIndex, GetTextSample(kSample2Id, kSample2Start,
|
||||
kSample2End, kSample2Payload))));
|
||||
|
||||
ASSERT_OK(Input(kInputIndex)->FlushAllDownstreams());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue