7 #include "packager/media/formats/webvtt/webvtt_parser.h" 12 #include "packager/base/logging.h" 13 #include "packager/base/strings/string_split.h" 14 #include "packager/base/strings/string_util.h" 15 #include "packager/media/base/text_stream_info.h" 16 #include "packager/media/formats/webvtt/webvtt_timestamp.h" 26 bool IsLikelyNote(
const std::string& line) {
27 return line ==
"NOTE" ||
28 base::StartsWith(line,
"NOTE ", base::CompareCase::SENSITIVE) ||
29 base::StartsWith(line,
"NOTE\t", base::CompareCase::SENSITIVE);
35 bool IsLikelyCueTiming(
const std::string& line) {
36 return line.find(
"-->") != std::string::npos;
44 bool MaybeCueId(
const std::string& line) {
45 return line.find(
"-->") == std::string::npos;
49 WebVttParser::WebVttParser(std::unique_ptr<FileReader> source,
50 const std::string& language)
51 : reader_(
std::move(source)), language_(language) {}
53 Status WebVttParser::InitializeInternal() {
57 bool WebVttParser::ValidateOutputStreamIndex(
size_t stream_index)
const {
59 return stream_index == 0;
62 Status WebVttParser::Run() {
65 : Status(error::INTERNAL_ERROR,
66 "Failed to parse WebVTT source. See log for details.");
69 void WebVttParser::Cancel() {
70 keep_reading_ =
false;
73 bool WebVttParser::Parse() {
74 std::vector<std::string> block;
75 if (!reader_.Next(&block)) {
76 LOG(ERROR) <<
"Failed to read WEBVTT HEADER - No blocks in source.";
82 if (block.size() != 1) {
83 LOG(ERROR) <<
"Failed to read WEBVTT header - " 84 <<
"block size should be 1 but was " << block.size() <<
".";
87 if (block[0] !=
"WEBVTT" && block[0] !=
"\xFE\xFFWEBVTT") {
88 LOG(ERROR) <<
"Failed to read WEBVTT header - should be WEBVTT but was " 93 const Status send_stream_info_result = DispatchTextStreamInfo();
95 if (send_stream_info_result != Status::OK) {
96 LOG(ERROR) <<
"Failed to send stream info down stream:" 97 << send_stream_info_result.error_message();
101 while (reader_.Next(&block) && keep_reading_) {
103 if (IsLikelyNote(block[0])) {
109 if (block.size() > 2 && MaybeCueId(block[0]) &&
110 IsLikelyCueTiming(block[1]) && ParseCueWithId(block)) {
115 if (block.size() > 1 && IsLikelyCueTiming(block[0]) &&
116 ParseCueWithNoId(block)) {
120 LOG(ERROR) <<
"Failed to determine block classification:";
121 LOG(ERROR) <<
" --- BLOCK START ---";
122 for (
const std::string& line : block) {
123 LOG(ERROR) <<
" " << line;
125 LOG(ERROR) <<
" --- BLOCK END ---";
129 return keep_reading_;
132 bool WebVttParser::ParseCueWithNoId(
const std::vector<std::string>& block) {
133 return ParseCue(
"", block.data(), block.size());
136 bool WebVttParser::ParseCueWithId(
const std::vector<std::string>& block) {
137 return ParseCue(block[0], block.data() + 1, block.size() - 1);
140 bool WebVttParser::ParseCue(
const std::string&
id,
141 const std::string* block,
143 std::shared_ptr<TextSample> sample(
new TextSample);
146 const std::vector<std::string> time_and_style = base::SplitString(
147 block[0],
" ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
151 if (time_and_style.size() >= 3 && time_and_style[1] ==
"-->" &&
152 WebVttTimestampToMs(time_and_style[0], &start_time) &&
153 WebVttTimestampToMs(time_and_style[2], &end_time)) {
154 sample->SetTime(start_time, end_time);
156 LOG(ERROR) <<
"Could not parse start time, -->, and end time from " 162 for (
size_t i = 3; i < time_and_style.size(); i++) {
163 sample->AppendStyle(time_and_style[i]);
167 for (
size_t i = 1; i < block_size; i++) {
168 sample->AppendPayload(block[i]);
173 if (send_result != Status::OK) {
174 LOG(ERROR) <<
"Failed to send text sample down stream:" 175 << send_result.error_message();
178 return send_result == Status::OK;
181 Status WebVttParser::DispatchTextStreamInfo() {
183 const int kTimescale = 1000;
188 const int kDuration = 0;
190 const char kWebVttCodecString[] =
"wvtt";
192 StreamInfo* info =
new TextStreamInfo(0, kTimescale, kDuration, kCodecWebVtt,
193 kWebVttCodecString,
"",
All the methods that are virtual are virtual for mocking.