diff --git a/packager/media/base/text_sample.h b/packager/media/base/text_sample.h index 98be5720c6..26297641ab 100644 --- a/packager/media/base/text_sample.h +++ b/packager/media/base/text_sample.h @@ -59,9 +59,14 @@ struct TextSettings { /// The position offset of the cue. For horizontal cues, this is the /// horizontal offset. Percent units are relative to the window. base::Optional position; - /// The size of the space used to draw text. For horizontal cues, this is the - /// width. Percent units are relative to the window. - base::Optional size; + /// For horizontal cues, this is the width of the area to draw cues. For + /// vertical cues, this is the height. Percent units are relative to the + /// window. + base::Optional width; + /// For horizontal cues, this is the height of the area to draw cues. For + /// vertical cues, this is the width. Percent units are relative to the + /// window. + base::Optional height; /// The region to draw the cue in. std::string region; diff --git a/packager/media/formats/ttml/ttml_generator.cc b/packager/media/formats/ttml/ttml_generator.cc index 4124aaefa7..fef7d87828 100644 --- a/packager/media/formats/ttml/ttml_generator.cc +++ b/packager/media/formats/ttml/ttml_generator.cc @@ -16,6 +16,8 @@ namespace ttml { namespace { +constexpr const char* kRegionIdPrefix = "_shaka_region_"; + std::string ToTtmlTime(int64_t time, uint32_t timescale) { int64_t remaining = time * 1000 / timescale; @@ -122,15 +124,31 @@ bool TtmlGenerator::AddSampleToXml(const TextSample& sample, RCHECK(p.SetStringAttribute("xml:id", sample.id())); const auto& settings = sample.settings(); - if (!settings.region.empty()) - RCHECK(p.SetStringAttribute("region", settings.region)); - if (settings.line || settings.position) { + if (settings.line || settings.position || settings.width || settings.height) { + // TTML positioning needs to be from a region. + if (!settings.region.empty()) { + LOG(WARNING) + << "Using both text regions and positioning isn't supported in TTML"; + } + const auto origin = ToTtmlSize( settings.position.value_or(TextNumber(0, TextUnitType::kPixels)), settings.line.value_or(TextNumber(0, TextUnitType::kPixels))); + const auto extent = ToTtmlSize( + settings.width.value_or(TextNumber(100, TextUnitType::kPercent)), + settings.height.value_or(TextNumber(100, TextUnitType::kPercent))); - RCHECK(p.SetStringAttribute("tts:origin", origin)); + const std::string id = kRegionIdPrefix + std::to_string(region_id_++); + xml::XmlNode region("region"); + RCHECK(region.SetStringAttribute("xml:id", id)); + RCHECK(region.SetStringAttribute("tts:origin", origin)); + RCHECK(region.SetStringAttribute("tts:extent", extent)); + RCHECK(p.SetStringAttribute("region", id)); + RCHECK(body->AddChild(std::move(region))); + } else if (!settings.region.empty()) { + RCHECK(p.SetStringAttribute("region", settings.region)); } + if (settings.writing_direction != WritingDirection::kHorizontal) { const char* dir = settings.writing_direction == WritingDirection::kVerticalGrowingLeft diff --git a/packager/media/formats/ttml/ttml_generator.h b/packager/media/formats/ttml/ttml_generator.h index d28e55a656..c0bc32e2f3 100644 --- a/packager/media/formats/ttml/ttml_generator.h +++ b/packager/media/formats/ttml/ttml_generator.h @@ -48,6 +48,8 @@ class TtmlGenerator { std::map regions_; std::string language_; uint32_t time_scale_; + // This is modified in "const" methods to create unique IDs. + mutable uint32_t region_id_ = 0; }; } // namespace ttml diff --git a/packager/media/formats/ttml/ttml_generator_unittest.cc b/packager/media/formats/ttml/ttml_generator_unittest.cc index 13d815ad1b..b17dcb8c09 100644 --- a/packager/media/formats/ttml/ttml_generator_unittest.cc +++ b/packager/media/formats/ttml/ttml_generator_unittest.cc @@ -190,8 +190,10 @@ TEST_F(TtmlMuxerTest, HandlesPosition) { " \n" " \n" "
\n" + " \n" "

bar

\n" + "end=\"00:00:06.00\" region=\"_shaka_region_0\">bar

\n" "
\n" " \n" "\n"; @@ -199,6 +201,8 @@ TEST_F(TtmlMuxerTest, HandlesPosition) { TestProperties properties; properties.settings.position.emplace(30, TextUnitType::kPercent); properties.settings.line.emplace(4, TextUnitType::kLines); + properties.settings.width.emplace(100, TextUnitType::kPixels); + properties.settings.height.emplace(1, TextUnitType::kLines); properties.body.body = "bar"; ParseSingleCue(kExpectedOutput, properties); diff --git a/packager/media/formats/webvtt/webvtt_parser.cc b/packager/media/formats/webvtt/webvtt_parser.cc index 449a7098c8..7dfbcb3de1 100644 --- a/packager/media/formats/webvtt/webvtt_parser.cc +++ b/packager/media/formats/webvtt/webvtt_parser.cc @@ -169,7 +169,7 @@ void ParseSettings(const std::string& id, } else if (id == "size") { float temp; if (ParsePercent(value, &temp)) { - settings->size.emplace(temp, TextUnitType::kPercent); + settings->width.emplace(temp, TextUnitType::kPercent); } else { LOG(WARNING) << "Invalid WebVTT size: " << value; } diff --git a/packager/media/formats/webvtt/webvtt_parser_unittest.cc b/packager/media/formats/webvtt/webvtt_parser_unittest.cc index 37dc3266e9..ce9612c95d 100644 --- a/packager/media/formats/webvtt/webvtt_parser_unittest.cc +++ b/packager/media/formats/webvtt/webvtt_parser_unittest.cc @@ -201,7 +201,8 @@ TEST_F(WebVttParserTest, ParseOneCue) { const auto& settings = samples_[0]->settings(); EXPECT_FALSE(settings.line); EXPECT_FALSE(settings.position); - EXPECT_FALSE(settings.size); + EXPECT_FALSE(settings.width); + EXPECT_FALSE(settings.height); EXPECT_EQ(settings.region, ""); EXPECT_EQ(settings.writing_direction, WritingDirection::kHorizontal); EXPECT_EQ(settings.text_alignment, TextAlignment::kCenter); @@ -320,9 +321,9 @@ TEST_F(WebVttParserTest, ParseSettingSize) { ASSERT_EQ(streams_.size(), 1u); ASSERT_EQ(samples_.size(), 1u); - ASSERT_TRUE(samples_[0]->settings().size); - EXPECT_EQ(samples_[0]->settings().size->type, TextUnitType::kPercent); - EXPECT_EQ(samples_[0]->settings().size->value, 50.0f); + ASSERT_TRUE(samples_[0]->settings().width); + EXPECT_EQ(samples_[0]->settings().width->type, TextUnitType::kPercent); + EXPECT_EQ(samples_[0]->settings().width->value, 50.0f); } TEST_F(WebVttParserTest, ParseOneCueWithManySettings) { @@ -343,7 +344,7 @@ TEST_F(WebVttParserTest, ParseOneCueWithManySettings) { EXPECT_EQ(samples_[0]->settings().writing_direction, WritingDirection::kVerticalGrowingRight); EXPECT_EQ(samples_[0]->settings().text_alignment, TextAlignment::kRight); - EXPECT_FALSE(samples_[0]->settings().size); + EXPECT_FALSE(samples_[0]->settings().width); ASSERT_TRUE(samples_[0]->settings().position); EXPECT_EQ(samples_[0]->settings().position->type, TextUnitType::kPercent); EXPECT_EQ(samples_[0]->settings().position->value, 20.0f); diff --git a/packager/media/formats/webvtt/webvtt_utils.cc b/packager/media/formats/webvtt/webvtt_utils.cc index 5cf1970c5b..2d23ca5498 100644 --- a/packager/media/formats/webvtt/webvtt_utils.cc +++ b/packager/media/formats/webvtt/webvtt_utils.cc @@ -215,15 +215,18 @@ std::string WebVttSettingsToString(const TextSettings& settings) { LOG(WARNING) << "WebVTT only supports percent position settings"; } } - if (settings.size) { - if (settings.size->type == TextUnitType::kPercent) { + if (settings.width) { + if (settings.width->type == TextUnitType::kPercent) { ret += " size:"; - ret += base::DoubleToString(settings.size->value); + ret += base::DoubleToString(settings.width->value); ret += "%"; } else { - LOG(WARNING) << "WebVTT only supports percent size settings"; + LOG(WARNING) << "WebVTT only supports percent width settings"; } } + if (settings.height) { + LOG(WARNING) << "WebVTT doesn't support cue heights"; + } if (settings.writing_direction != WritingDirection::kHorizontal) { ret += " direction:"; if (settings.writing_direction == WritingDirection::kVerticalGrowingLeft) { diff --git a/packager/media/formats/webvtt/webvtt_utils_unittest.cc b/packager/media/formats/webvtt/webvtt_utils_unittest.cc index ab2997f13d..bb405b9cb9 100644 --- a/packager/media/formats/webvtt/webvtt_utils_unittest.cc +++ b/packager/media/formats/webvtt/webvtt_utils_unittest.cc @@ -151,7 +151,7 @@ TEST(WebVttUtilsTest, SettingsToString) { settings.region = "foo"; settings.line = TextNumber(27, TextUnitType::kPercent); settings.position = TextNumber(42, TextUnitType::kPercent); - settings.size = TextNumber(54, TextUnitType::kPercent); + settings.width = TextNumber(54, TextUnitType::kPercent); settings.writing_direction = WritingDirection::kVerticalGrowingLeft; settings.text_alignment = TextAlignment::kEnd;