Implement MsToWebVTTTimestamp

Implemented the other end of the webvtt timestamp parsing as it will
be needed when we write out WebVtt files. Also changed the name of
the parse function to be more inline with the writing function.

Change-Id: I1f36ddbbf80028732b4cb1b15e871dec17767f63
This commit is contained in:
Aaron Vaage 2017-08-17 11:09:34 -07:00
parent f20ad03caf
commit ec83d8a73a
4 changed files with 83 additions and 33 deletions

View File

@ -93,14 +93,14 @@ bool ParseTimingAndSettingsLine(const std::string& line,
} }
const std::string& start_time_str = entries[0]; const std::string& start_time_str = entries[0];
if (!WebVttTimestampParse(start_time_str, start_time)) { if (!WebVttTimestampToMs(start_time_str, start_time)) {
LOG(ERROR) << "Failed to parse " << start_time_str << " in " << line; LOG(ERROR) << "Failed to parse " << start_time_str << " in " << line;
return false; return false;
} }
const std::string& end_time_str = entries[2]; const std::string& end_time_str = entries[2];
uint64_t end_time = 0; uint64_t end_time = 0;
if (!WebVttTimestampParse(end_time_str, &end_time)) { if (!WebVttTimestampToMs(end_time_str, &end_time)) {
LOG(ERROR) << "Failed to parse " << end_time_str << " in " << line; LOG(ERROR) << "Failed to parse " << end_time_str << " in " << line;
return false; return false;
} }

View File

@ -7,9 +7,11 @@
#include "packager/media/formats/webvtt/webvtt_timestamp.h" #include "packager/media/formats/webvtt/webvtt_timestamp.h"
#include <ctype.h> #include <ctype.h>
#include <inttypes.h>
#include "packager/base/logging.h" #include "packager/base/logging.h"
#include "packager/base/strings/string_number_conversions.h" #include "packager/base/strings/string_number_conversions.h"
#include "packager/base/strings/stringprintf.h"
namespace shaka { namespace shaka {
namespace media { namespace media {
@ -22,22 +24,17 @@ bool GetTotalMilliseconds(uint64_t hours,
uint64_t* out) { uint64_t* out) {
DCHECK(out); DCHECK(out);
if (minutes > 59 || seconds > 59 || ms > 999) { if (minutes > 59 || seconds > 59 || ms > 999) {
VLOG(1) << "Hours:" << hours VLOG(1) << "Hours:" << hours << " Minutes:" << minutes
<< " Minutes:" << minutes << " Seconds:" << seconds << " MS:" << ms
<< " Seconds:" << seconds
<< " MS:" << ms
<< " shoud have never made it to GetTotalMilliseconds"; << " shoud have never made it to GetTotalMilliseconds";
return false; return false;
} }
*out = 60 * 60 * 1000 * hours + *out = 60 * 60 * 1000 * hours + 60 * 1000 * minutes + 1000 * seconds + ms;
60 * 1000 * minutes +
1000 * seconds +
ms;
return true; return true;
} }
} // namespace } // namespace
bool WebVttTimestampParse(const base::StringPiece& source, uint64_t* out) { bool WebVttTimestampToMs(const base::StringPiece& source, uint64_t* out) {
DCHECK(out); DCHECK(out);
if (source.length() < 9) { if (source.length() < 9) {
@ -54,12 +51,11 @@ bool WebVttTimestampParse(const base::StringPiece& source, uint64_t* out) {
uint64_t seconds = 0; uint64_t seconds = 0;
uint64_t ms = 0; uint64_t ms = 0;
const bool has_hours = minutes_begin >= 3 && const bool has_hours =
source[minutes_begin-1] == ':' && minutes_begin >= 3 && source[minutes_begin - 1] == ':' &&
base::StringToUint64(source.substr(0, minutes_begin - 1), &hours); base::StringToUint64(source.substr(0, minutes_begin - 1), &hours);
if ((minutes_begin == 0 || has_hours) && if ((minutes_begin == 0 || has_hours) && source[seconds_begin - 1] == ':' &&
source[seconds_begin-1] == ':' &&
source[milliseconds_begin - 1] == '.' && source[milliseconds_begin - 1] == '.' &&
base::StringToUint64(source.substr(minutes_begin, 2), &minutes) && base::StringToUint64(source.substr(minutes_begin, 2), &minutes) &&
base::StringToUint64(source.substr(seconds_begin, 2), &seconds) && base::StringToUint64(source.substr(seconds_begin, 2), &seconds) &&
@ -70,5 +66,21 @@ bool WebVttTimestampParse(const base::StringPiece& source, uint64_t* out) {
LOG(WARNING) << "Timestamp '" << source << "' is mal-formed"; LOG(WARNING) << "Timestamp '" << source << "' is mal-formed";
return false; return false;
} }
std::string MsToWebVttTimestamp(uint64_t ms) {
uint64_t remaining = ms;
uint64_t only_ms = remaining % 1000;
remaining /= 1000;
uint64_t only_seconds = remaining % 60;
remaining /= 60;
uint64_t only_minutes = remaining % 60;
remaining /= 60;
uint64_t only_hours = remaining;
return base::StringPrintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64
".%03" PRIu64,
only_hours, only_minutes, only_seconds, only_ms);
}
} // namespace media } // namespace media
} // namespace shaka } // namespace shaka

View File

@ -18,7 +18,10 @@ namespace media {
// Parse a timestamp into milliseconds using the two patterns defined by WebVtt: // Parse a timestamp into milliseconds using the two patterns defined by WebVtt:
// LONG : ##:##:##.### (long can have 2 or more hour digits) // LONG : ##:##:##.### (long can have 2 or more hour digits)
// SHORT : ##:##:### // SHORT : ##:##:###
bool WebVttTimestampParse(const base::StringPiece& source, uint64_t* out); bool WebVttTimestampToMs(const base::StringPiece& source, uint64_t* out);
// Create a long form timestamp encoded as a string.
std::string MsToWebVttTimestamp(uint64_t ms);
} // namespace media } // namespace media
} // namespace shaka } // namespace shaka

View File

@ -13,84 +13,119 @@ namespace media {
TEST(WebVttTimestampTest, TooShort) { TEST(WebVttTimestampTest, TooShort) {
uint64_t ms; uint64_t ms;
EXPECT_FALSE(WebVttTimestampParse("00.000", &ms)); EXPECT_FALSE(WebVttTimestampToMs("00.000", &ms));
} }
TEST(WebVttTimestampTest, RightLengthButMeaningless) { TEST(WebVttTimestampTest, RightLengthButMeaningless) {
uint64_t ms; uint64_t ms;
EXPECT_FALSE(WebVttTimestampParse("ABCDEFGHI", &ms)); EXPECT_FALSE(WebVttTimestampToMs("ABCDEFGHI", &ms));
} }
TEST(WebVttTimestampTest, ParseHours) { TEST(WebVttTimestampTest, ParseHours) {
uint64_t ms; uint64_t ms;
EXPECT_TRUE(WebVttTimestampParse("12:00:00.000", &ms)); EXPECT_TRUE(WebVttTimestampToMs("12:00:00.000", &ms));
EXPECT_EQ(43200000u, ms); EXPECT_EQ(43200000u, ms);
} }
TEST(WebVttTimestampTest, ParseLongHours) { TEST(WebVttTimestampTest, ParseLongHours) {
uint64_t ms; uint64_t ms;
EXPECT_TRUE(WebVttTimestampParse("120:00:00.000", &ms)); EXPECT_TRUE(WebVttTimestampToMs("120:00:00.000", &ms));
EXPECT_EQ(432000000u, ms); EXPECT_EQ(432000000u, ms);
} }
TEST(WebVttTimestampTest, ParseMinutes) { TEST(WebVttTimestampTest, ParseMinutes) {
uint64_t ms; uint64_t ms;
EXPECT_TRUE(WebVttTimestampParse("00:12:00.000", &ms)); EXPECT_TRUE(WebVttTimestampToMs("00:12:00.000", &ms));
EXPECT_EQ(720000u, ms); EXPECT_EQ(720000u, ms);
} }
TEST(WebVttTimestampTest, ParseSeconds) { TEST(WebVttTimestampTest, ParseSeconds) {
uint64_t ms; uint64_t ms;
EXPECT_TRUE(WebVttTimestampParse("00:00:12.000", &ms)); EXPECT_TRUE(WebVttTimestampToMs("00:00:12.000", &ms));
EXPECT_EQ(12000u, ms); EXPECT_EQ(12000u, ms);
} }
TEST(WebVttTimestampTest, ParseMs) { TEST(WebVttTimestampTest, ParseMs) {
uint64_t ms; uint64_t ms;
EXPECT_TRUE(WebVttTimestampParse("00:00:00.123", &ms)); EXPECT_TRUE(WebVttTimestampToMs("00:00:00.123", &ms));
EXPECT_EQ(123u, ms); EXPECT_EQ(123u, ms);
} }
TEST(WebVttTimestampTest, ParseNoHours) { TEST(WebVttTimestampTest, ParseNoHours) {
uint64_t ms; uint64_t ms;
EXPECT_TRUE(WebVttTimestampParse("12:00.000", &ms)); EXPECT_TRUE(WebVttTimestampToMs("12:00.000", &ms));
EXPECT_EQ(720000u, ms); EXPECT_EQ(720000u, ms);
} }
TEST(WebVttTimestampTest, FailWithShortHours) { TEST(WebVttTimestampTest, FailWithShortHours) {
uint64_t ms; uint64_t ms;
EXPECT_FALSE(WebVttTimestampParse("1:00:00.000", &ms)); EXPECT_FALSE(WebVttTimestampToMs("1:00:00.000", &ms));
} }
TEST(WebVttTimestampTest, FailWithShortMinutes) { TEST(WebVttTimestampTest, FailWithShortMinutes) {
uint64_t ms; uint64_t ms;
EXPECT_FALSE(WebVttTimestampParse("00:1:00.000", &ms)); EXPECT_FALSE(WebVttTimestampToMs("00:1:00.000", &ms));
} }
TEST(WebVttTimestampTest, FailWithShortSeconds) { TEST(WebVttTimestampTest, FailWithShortSeconds) {
uint64_t ms; uint64_t ms;
EXPECT_FALSE(WebVttTimestampParse("00:1.000", &ms)); EXPECT_FALSE(WebVttTimestampToMs("00:1.000", &ms));
} }
TEST(WebVttTimestampTest, FailWithShortMs) { TEST(WebVttTimestampTest, FailWithShortMs) {
uint64_t ms; uint64_t ms;
EXPECT_FALSE(WebVttTimestampParse("00:00.01", &ms)); EXPECT_FALSE(WebVttTimestampToMs("00:00.01", &ms));
} }
TEST(WebVttTimestampTest, FailWithNonDigit) { TEST(WebVttTimestampTest, FailWithNonDigit) {
uint64_t ms; uint64_t ms;
EXPECT_FALSE(WebVttTimestampParse("00:0A:00.000", &ms)); EXPECT_FALSE(WebVttTimestampToMs("00:0A:00.000", &ms));
} }
TEST(WebVttTimestampTest, FailWithInvalidMinutes) { TEST(WebVttTimestampTest, FailWithInvalidMinutes) {
uint64_t ms; uint64_t ms;
EXPECT_FALSE(WebVttTimestampParse("00:79:00.000", &ms)); EXPECT_FALSE(WebVttTimestampToMs("00:79:00.000", &ms));
} }
TEST(WebVttTimestampTest, FailWithInvalidSeconds) { TEST(WebVttTimestampTest, FailWithInvalidSeconds) {
uint64_t ms; uint64_t ms;
EXPECT_FALSE(WebVttTimestampParse("00:00:79.000", &ms)); EXPECT_FALSE(WebVttTimestampToMs("00:00:79.000", &ms));
} }
TEST(WebVttTimestampTest, CreatesMilliseconds) {
EXPECT_EQ("00:00:00.123", MsToWebVttTimestamp(123));
}
TEST(WebVttTimestampTest, CreatesMillisecondsShort) {
EXPECT_EQ("00:00:00.012", MsToWebVttTimestamp(12));
}
TEST(WebVttTimestampTest, CreateSeconds) {
EXPECT_EQ("00:00:12.000", MsToWebVttTimestamp(12000));
}
TEST(WebVttTimestampTest, CreateSecondsShort) {
EXPECT_EQ("00:00:01.000", MsToWebVttTimestamp(1000));
}
TEST(WebVttTimestampTest, CreateMinutes) {
EXPECT_EQ("00:12:00.000", MsToWebVttTimestamp(720000));
}
TEST(WebVttTimestampTest, CreateMinutesShort) {
EXPECT_EQ("00:01:00.000", MsToWebVttTimestamp(60000));
}
TEST(WebVttTimestampTest, CreateHours) {
EXPECT_EQ("12:00:00.000", MsToWebVttTimestamp(43200000));
}
TEST(WebVttTimestampTest, CreateHoursShort) {
EXPECT_EQ("01:00:00.000", MsToWebVttTimestamp(3600000));
}
TEST(WebVttTimestampTest, CreateHoursLong) {
EXPECT_EQ("123:00:00.000", MsToWebVttTimestamp(442800000));
}
} // namespace media } // namespace media
} // namespace shaka } // namespace shaka