7 #include "packager/media/formats/webvtt/webvtt_sample_converter.h"
12 #include "packager/base/strings/string_util.h"
13 #include "packager/base/strings/stringprintf.h"
14 #include "packager/media/base/buffer_writer.h"
15 #include "packager/media/base/media_sample.h"
16 #include "packager/media/formats/mp4/box_buffer.h"
17 #include "packager/media/formats/mp4/box_definitions.h"
24 std::shared_ptr<MediaSample> CreateEmptyCueSample(uint64_t start_time,
26 DCHECK_GT(end_time, start_time);
27 mp4::VTTEmptyCueBox empty_cue_box;
29 std::vector<uint8_t> serialized;
30 AppendBoxToVector(&empty_cue_box, &serialized);
33 serialized.data(), serialized.size(),
false);
34 empty_cue_sample->set_pts(start_time);
35 empty_cue_sample->set_duration(end_time - start_time);
36 return empty_cue_sample;
39 mp4::VTTCueBox CueBoxFromCue(
const Cue& cue) {
40 mp4::VTTCueBox cue_box;
41 if (!cue.identifier.empty()) {
42 cue_box.cue_id.cue_id = cue.identifier;
45 if (!cue.settings.empty()) {
46 cue_box.cue_settings.settings = cue.settings;
49 cue_box.cue_payload.cue_text = cue.payload.front();
53 std::string TimeToWebVttTimeStamp(uint64_t time_in_ms) {
54 const int milliseconds = time_in_ms % 1000;
55 const uint64_t seconds_left = time_in_ms / 1000;
56 const int seconds = seconds_left % 60;
57 const uint64_t minutes_left = seconds_left / 60;
58 const int minutes = minutes_left % 60;
59 const int hours = minutes_left / 60;
61 return base::StringPrintf(
"%02d:%02d:%02d.%03d", hours, minutes, seconds,
65 std::shared_ptr<MediaSample> CreateVTTCueBoxesSample(
66 const std::list<const Cue*>& cues,
74 std::vector<uint8_t> data;
75 std::string cue_current_time = TimeToWebVttTimeStamp(start_time);
78 for (
const Cue* cue : cues) {
79 mp4::VTTCueBox cue_box = CueBoxFromCue(*cue);
85 cue_box.Write(&writer);
88 std::shared_ptr<MediaSample> sample =
90 sample->set_pts(start_time);
91 sample->set_duration(end_time - start_time);
97 uint64_t GetMinimumPastSweepLine(uint64_t cue_start_time,
98 uint64_t cue_end_time,
100 uint64_t current_minimum) {
101 DCHECK_GE(current_minimum, sweep_line);
102 if (cue_end_time <= sweep_line)
103 return current_minimum;
106 if (cue_start_time > sweep_line) {
108 return std::min(cue_start_time, current_minimum);
111 return std::min(cue_end_time, current_minimum);
117 void AppendBoxToVector(mp4::Box* box, std::vector<uint8_t>* output_vector) {
120 output_vector->insert(output_vector->end(),
122 writer.Buffer() + writer.Size());
125 WebVttSampleConverter::WebVttSampleConverter() : next_cue_start_time_(0u) {}
126 WebVttSampleConverter::~WebVttSampleConverter() {}
131 if (sample->data_size() == 0u) {
134 comment.cue_additional_text.assign(
135 sample->side_data(), sample->side_data() + sample->side_data_size());
136 additional_texts_.push_back(comment);
142 cues_.push_back(MediaSampleToCue(*sample));
143 if (cues_.size() == 1) {
146 next_cue_start_time_ = cues_.front().start_time;
150 CHECK_GE(cues_.size(), 2u);
157 bool processed_cues = HandleAllCuesButLatest();
162 auto erase_last_iterator = --cues_.end();
163 cues_.erase(cues_.begin(), erase_last_iterator);
169 if (cues_.size() == 1) {
170 std::list<const Cue*> temp_list;
171 temp_list.push_back(&cues_.front());
172 CHECK_EQ(next_cue_start_time_, cues_.front().start_time);
173 ready_samples_.push_back(CreateVTTCueBoxesSample(
175 next_cue_start_time_,
176 cues_.front().start_time + cues_.front().duration));
181 bool processed_cue = HandleAllCues();
183 <<
"No cues were processed but the cues should have been flushed.";
188 return ready_samples_.size();
192 CHECK(!ready_samples_.empty());
193 std::shared_ptr<MediaSample> ret = ready_samples_.front();
194 ready_samples_.pop_front();
210 bool WebVttSampleConverter::HandleAllCuesButLatest() {
211 DCHECK_GE(cues_.size(), 2u);
212 const Cue& latest_cue = cues_.back();
216 uint64_t max_cue_end_time = 0;
217 auto latest_cue_it = --cues_.end();
218 for (
auto cue_it = cues_.begin(); cue_it != latest_cue_it; ++cue_it) {
219 const Cue& cue = *cue_it;
220 const uint64_t cue_end_time = cue.start_time + cue.duration;
221 if (cue_end_time > latest_cue.start_time)
224 if (max_cue_end_time < cue_end_time)
225 max_cue_end_time = cue_end_time;
235 const uint64_t sweep_stop_time = max_cue_end_time;
236 const uint64_t sweep_line_start = cues_.front().start_time;
237 bool processed_cues =
238 SweepCues(sweep_line_start, sweep_stop_time);
239 next_cue_start_time_ = sweep_stop_time;
240 if (next_cue_start_time_ < latest_cue.start_time) {
241 ready_samples_.push_back(CreateEmptyCueSample(next_cue_start_time_,
242 latest_cue.start_time));
243 next_cue_start_time_ = latest_cue.start_time;
245 return processed_cues;
248 bool WebVttSampleConverter::HandleAllCues() {
249 uint64_t latest_time = 0u;
250 for (
const Cue& cue : cues_) {
251 if (cue.start_time + cue.duration > latest_time)
252 latest_time = cue.start_time + cue.duration;
254 const uint64_t sweep_line_start = cues_.front().start_time;
255 const uint64_t sweep_stop_time = latest_time;
256 bool processed = SweepCues(sweep_line_start, sweep_stop_time);
257 next_cue_start_time_ = sweep_stop_time;
261 bool WebVttSampleConverter::SweepCues(uint64_t sweep_line,
262 uint64_t sweep_stop_time) {
263 bool processed_cues =
false;
269 while (sweep_line < sweep_stop_time) {
270 std::list<const Cue*> cues_for_a_sample;
271 uint64_t next_start_time = sweep_stop_time;
276 for (
const Cue& cue : cues_) {
277 if (cue.start_time >= sweep_stop_time)
279 if (cue.start_time >= next_start_time)
282 const uint64_t cue_end_time = cue.start_time + cue.duration;
283 if (cue_end_time <= sweep_line)
285 next_start_time = GetMinimumPastSweepLine(
286 cue.start_time, cue_end_time, sweep_line, next_start_time);
288 if (cue.start_time <= sweep_line) {
289 DCHECK_GT(cue_end_time, sweep_line);
290 cues_for_a_sample.push_back(&cue);
294 DCHECK(!cues_for_a_sample.empty()) <<
"For now the only use case of this "
295 "function is to sweep non-empty "
297 if (!cues_for_a_sample.empty()) {
298 ready_samples_.push_back(CreateVTTCueBoxesSample(
299 cues_for_a_sample, sweep_line, next_start_time));
300 processed_cues =
true;
303 sweep_line = next_start_time;
306 DCHECK_EQ(sweep_line, sweep_stop_time);
307 return processed_cues;