7 #include "packager/media/formats/webvtt/webvtt_media_parser.h"
12 #include "packager/base/logging.h"
13 #include "packager/base/strings/string_number_conversions.h"
14 #include "packager/base/strings/string_split.h"
15 #include "packager/base/strings/string_util.h"
16 #include "packager/media/base/macros.h"
17 #include "packager/media/base/media_sample.h"
18 #include "packager/media/base/text_stream_info.h"
19 #include "packager/media/formats/webvtt/webvtt_timestamp.h"
26 const bool kFlush =
true;
29 const int kTrackId = 0;
31 const char kCR = 0x0D;
32 const char kLF = 0x0A;
37 bool ReadLine(std::string* data, std::string* line) {
38 if (data->size() == 0) {
41 size_t string_position = 0;
43 int line_break_length = 1;
44 bool found_line_break =
false;
45 while (string_position < data->size()) {
46 if (data->at(string_position) == kLF) {
47 found_line_break =
true;
51 if (data->at(string_position) == kCR) {
52 found_line_break =
true;
53 if (string_position + 1 >= data->size())
56 if (data->at(string_position + 1) == kLF)
57 line_break_length = 2;
64 if (!found_line_break)
67 *line = data->substr(0, string_position);
68 data->erase(0, string_position + line_break_length);
74 bool ParseTimingAndSettingsLine(
const std::string& line,
77 std::string* settings) {
81 std::vector<std::string> entries = base::SplitString(
82 line,
" ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
83 if (entries.size() < 3) {
86 LOG(ERROR) <<
"Not enough tokens to be a timing " << line;
90 if (entries[1] !=
"-->") {
91 LOG(ERROR) <<
"Cannot find an arrow at the right place " << line;
95 const std::string& start_time_str = entries[0];
96 if (!WebVttTimestampParse(start_time_str, start_time)) {
97 LOG(ERROR) <<
"Failed to parse " << start_time_str <<
" in " << line;
101 const std::string& end_time_str = entries[2];
102 uint64_t end_time = 0;
103 if (!WebVttTimestampParse(end_time_str, &end_time)) {
104 LOG(ERROR) <<
"Failed to parse " << end_time_str <<
" in " << line;
107 *duration = end_time - *start_time;
109 entries.erase(entries.begin(), entries.begin() + 3);
110 *settings = base::JoinString(entries,
" ");
116 WebVttMediaParser::WebVttMediaParser()
117 : state_(kHeader), sample_converter_(new WebVttSampleConverter()) {}
118 WebVttMediaParser::~WebVttMediaParser() {}
124 new_sample_cb_ = new_sample_cb;
129 if (state_ != kCuePayload && state_ != kComment)
132 if (!data_.empty()) {
135 if (state_ == kCuePayload) {
136 current_cue_.payload += data_ +
"\n";
138 current_cue_.comment += data_ +
"\n";
143 if (!ProcessCurrentCue(kFlush)) {
144 state_ = kParseError;
148 state_ = kCueIdentifierOrTimingOrComment;
153 if (state_ == kParseError) {
154 LOG(WARNING) <<
"The parser is in an error state, ignoring input.";
158 data_.insert(data_.end(), buf, buf + size);
161 while (ReadLine(&data_, &line)) {
164 const bool has_arrow = line.find(
"-->") != std::string::npos;
165 if (state_ == kCueTiming) {
167 LOG(ERROR) <<
"Expected --> in: " << line;
168 state_ = kParseError;
171 }
else if (state_ != kCueIdentifierOrTimingOrComment) {
173 LOG(ERROR) <<
"Unexpected --> in " << line;
174 state_ = kParseError;
182 header_.push_back(line);
187 std::vector<std::shared_ptr<StreamInfo>> streams;
189 const int kTimescale = 1000;
194 const int kDuration = 0;
198 const char kLanguage[] =
"";
200 const char kWebVttCodecString[] =
"wvtt";
201 streams.emplace_back(
203 kCodecWebVtt, kWebVttCodecString,
204 base::JoinString(header_,
"\n"),
209 init_cb_.Run(streams);
210 state_ = kCueIdentifierOrTimingOrComment;
214 header_.push_back(line);
217 case kCueIdentifierOrTimingOrComment: {
225 if (base::StartsWith(line,
"NOTE",
226 base::CompareCase::INSENSITIVE_ASCII)) {
228 current_cue_.comment += line +
"\n";
232 current_cue_.identifier = line;
243 FALLTHROUGH_INTENDED;
247 if (!ParseTimingAndSettingsLine(line, ¤t_cue_.start_time,
248 ¤t_cue_.duration,
249 ¤t_cue_.settings)) {
250 state_ = kParseError;
253 state_ = kCuePayload;
258 state_ = kCueIdentifierOrTimingOrComment;
259 if (!ProcessCurrentCue(!kFlush)) {
260 state_ = kParseError;
266 current_cue_.payload += line +
"\n";
271 state_ = kCueIdentifierOrTimingOrComment;
272 if (!ProcessCurrentCue(!kFlush)) {
273 state_ = kParseError;
279 current_cue_.comment += line +
"\n";
291 void WebVttMediaParser::InjectWebVttSampleConvertForTesting(
292 std::unique_ptr<WebVttSampleConverter> converter) {
293 sample_converter_ = std::move(converter);
296 bool WebVttMediaParser::ProcessCurrentCue(
bool flush) {
297 sample_converter_->PushCue(current_cue_);
298 current_cue_ = Cue();
300 sample_converter_->Flush();
302 while (sample_converter_->ReadySamplesSize() > 0) {
303 if (!new_sample_cb_.Run(kTrackId, sample_converter_->PopSample())) {
304 LOG(ERROR) <<
"New sample callback failed.";