7 #include <packager/media/formats/webvtt/webvtt_to_mp4_handler.h>
12 #include <absl/log/check.h>
14 #include <packager/macros/logging.h>
15 #include <packager/macros/status.h>
16 #include <packager/media/base/buffer_writer.h>
17 #include <packager/media/formats/mp4/box_buffer.h>
18 #include <packager/media/formats/mp4/box_definitions.h>
19 #include <packager/media/formats/webvtt/webvtt_utils.h>
26 enum class DisplayActionType { ADD, REMOVE };
28 struct DisplayAction {
29 DisplayActionType type;
30 const TextSample* sample;
33 std::multimap<int64_t, DisplayAction> CreateActionList(
34 int64_t segment_start,
36 const std::list<std::shared_ptr<const TextSample>>& samples) {
37 std::multimap<int64_t, DisplayAction> actions;
39 for (
const auto& sample : samples) {
44 DCHECK_LT(sample->start_time(), segment_end);
46 {sample->start_time(), {DisplayActionType::ADD, sample.get()}});
50 if (sample->EndTime() < segment_end) {
52 {sample->EndTime(), {DisplayActionType::REMOVE, sample.get()}});
59 void WriteSample(
const TextSample& sample, BufferWriter* out) {
62 if (sample.id().length()) {
63 box.cue_id.cue_id = sample.id();
65 box.cue_settings.settings = WebVttSettingsToString(sample.settings());
66 box.cue_payload.cue_text = WebVttFragmentToString(sample.body());
76 void WriteSamples(
const std::list<const TextSample*>& samples,
77 BufferWriter* writer) {
78 DCHECK_GE(samples.size(), 0u);
80 for (
const auto& sample : samples) {
81 WriteSample(*sample, writer);
85 void WriteEmptySample(BufferWriter* writer) {
86 mp4::VTTEmptyCueBox box;
90 std::shared_ptr<MediaSample> CreateMediaSample(
const BufferWriter& buffer,
93 DCHECK_GE(start_time, 0);
94 DCHECK_GT(end_time, start_time);
96 const bool kIsKeyFrame =
true;
98 std::shared_ptr<MediaSample> sample =
100 sample->set_pts(start_time);
101 sample->set_dts(start_time);
102 sample->set_duration(end_time - start_time);
108 Status WebVttToMp4Handler::InitializeInternal() {
112 Status WebVttToMp4Handler::Process(std::unique_ptr<StreamData> stream_data) {
113 switch (stream_data->stream_data_type) {
114 case StreamDataType::kStreamInfo:
115 return OnStreamInfo(std::move(stream_data));
116 case StreamDataType::kCueEvent:
117 return OnCueEvent(std::move(stream_data));
118 case StreamDataType::kSegmentInfo:
119 return OnSegmentInfo(std::move(stream_data));
120 case StreamDataType::kTextSample:
121 return OnTextSample(std::move(stream_data));
123 return Status(error::INTERNAL_ERROR,
124 "Invalid stream data type (" +
125 StreamDataTypeToString(stream_data->stream_data_type) +
126 ") for this WebVttToMp4 handler");
130 Status WebVttToMp4Handler::OnStreamInfo(
131 std::unique_ptr<StreamData> stream_data) {
133 DCHECK(stream_data->stream_info);
135 auto clone = stream_data->stream_info->Clone();
136 clone->set_codec(kCodecWebVtt);
137 clone->set_codec_string(
"wvtt");
139 if (clone->stream_type() != kStreamText) {
140 return Status(error::MUXER_FAILURE,
"Incorrect stream type");
144 StreamData::FromStreamInfo(stream_data->stream_index, std::move(clone)));
147 Status WebVttToMp4Handler::OnCueEvent(std::unique_ptr<StreamData> stream_data) {
149 DCHECK(stream_data->cue_event);
151 if (current_segment_.size()) {
152 return Status(error::INTERNAL_ERROR,
153 "Cue Events should come right after segment info.");
156 return Dispatch(std::move(stream_data));
159 Status WebVttToMp4Handler::OnSegmentInfo(
160 std::unique_ptr<StreamData> stream_data) {
162 DCHECK(stream_data->segment_info);
164 const auto& segment = stream_data->segment_info;
166 int64_t segment_start = segment->start_timestamp;
167 int64_t segment_duration = segment->duration;
168 int64_t segment_end = segment_start + segment_duration;
170 RETURN_IF_ERROR(DispatchCurrentSegment(segment_start, segment_end));
171 current_segment_.clear();
173 return Dispatch(std::move(stream_data));
176 Status WebVttToMp4Handler::OnTextSample(
177 std::unique_ptr<StreamData> stream_data) {
179 DCHECK(stream_data->text_sample);
181 auto& sample = stream_data->text_sample;
185 if (sample->body().is_empty()) {
191 current_segment_.push_back(std::move(stream_data->text_sample));
195 Status WebVttToMp4Handler::DispatchCurrentSegment(int64_t segment_start,
196 int64_t segment_end) {
199 std::list<const TextSample*> active;
203 int section_start = segment_start;
206 auto actions = CreateActionList(segment_start, segment_end, current_segment_);
207 auto front = actions.begin();
212 while (section_start < segment_end) {
218 while (front != actions.end() && front->first <= section_start) {
219 auto& action = front->second;
221 switch (action.type) {
222 case DisplayActionType::ADD: {
223 active.push_back(action.sample);
226 case DisplayActionType::REMOVE: {
227 auto found = std::find(active.begin(), active.end(), action.sample);
228 DCHECK(found != active.end());
233 NOTIMPLEMENTED() <<
"Unsupported DisplayActionType "
234 <<
static_cast<int>(action.type);
245 int64_t section_end = front == actions.end() ? segment_end : front->first;
246 DCHECK_GT(section_end, section_start);
247 DCHECK_LE(section_end, segment_end);
248 RETURN_IF_ERROR(MergeDispatchSamples(section_start, section_end, active));
250 section_start = section_end;
253 DCHECK(front == actions.end()) <<
"We should have processed all actions.";
258 Status WebVttToMp4Handler::MergeDispatchSamples(
261 const std::list<const TextSample*>& state) {
262 DCHECK_GT(end_time, start_time);
267 WriteSamples(state, &box_writer_);
269 WriteEmptySample(&box_writer_);
273 kTrackId, CreateMediaSample(box_writer_, start_time, end_time));
All the methods that are virtual are virtual for mocking.