diff --git a/packager/hls/base/master_playlist.cc b/packager/hls/base/master_playlist.cc index 1994961cc6..761cc6df75 100644 --- a/packager/hls/base/master_playlist.cc +++ b/packager/hls/base/master_playlist.cc @@ -124,7 +124,7 @@ void BuildVideoTag(const MediaPlaylist& playlist, tag.AddNumber("BANDWIDTH", bitrate); tag.AddQuotedString("CODECS", codecs); - tag.AddResolution("RESOLUTION", width, height); + tag.AddNumberPair("RESOLUTION", width, 'x', height); if (audio_group_id) { tag.AddQuotedString("AUDIO", *audio_group_id); diff --git a/packager/hls/base/media_playlist.cc b/packager/hls/base/media_playlist.cc index 603ae9aaa8..417c985c70 100644 --- a/packager/hls/base/media_playlist.cc +++ b/packager/hls/base/media_playlist.cc @@ -16,6 +16,7 @@ #include "packager/base/strings/string_number_conversions.h" #include "packager/base/strings/stringprintf.h" #include "packager/file/file.h" +#include "packager/hls/base/tag.h" #include "packager/media/base/language_utils.h" #include "packager/version/version.h" @@ -35,28 +36,29 @@ uint32_t GetTimeScale(const MediaInfo& media_info) { return 0u; } -std::string CreateExtXMap(const MediaInfo& media_info) { - std::string ext_x_map; +void AppendExtXMap(const MediaInfo& media_info, std::string* out) { if (media_info.has_init_segment_name()) { - base::StringAppendF(&ext_x_map, "#EXT-X-MAP:URI=\"%s\"", - media_info.init_segment_name().data()); + Tag tag("#EXT-X-MAP", out); + tag.AddQuotedString("URI", media_info.init_segment_name().data()); + out->append("\n"); } else if (media_info.has_media_file_name() && media_info.has_init_range()) { // It only makes sense for single segment media to have EXT-X-MAP if // there is init_range. - base::StringAppendF(&ext_x_map, "#EXT-X-MAP:URI=\"%s\"", - media_info.media_file_name().data()); + Tag tag("#EXT-X-MAP", out); + tag.AddQuotedString("URI", media_info.media_file_name().data()); + + if (media_info.has_init_range()) { + const uint64_t begin = media_info.init_range().begin(); + const uint64_t end = media_info.init_range().end(); + const uint64_t length = end - begin + 1; + + tag.AddQuotedNumberPair("BYTERANGE", length, '@', begin); + } + + out->append("\n"); } else { - return ""; + // This media info does not need an ext-x-map tag. } - if (media_info.has_init_range()) { - const uint64_t begin = media_info.init_range().begin(); - const uint64_t end = media_info.init_range().end(); - const uint64_t length = end - begin + 1; - base::StringAppendF(&ext_x_map, ",BYTERANGE=\"%" PRIu64 "@%" PRIu64 "\"", - length, begin); - } - ext_x_map += "\n"; - return ext_x_map; } std::string CreatePlaylistHeader( @@ -109,7 +111,8 @@ std::string CreatePlaylistHeader( // Put EXT-X-MAP at the end since the rest of the playlist is about the // segment and key info. - header += CreateExtXMap(media_info); + AppendExtXMap(media_info, &header); + return header; } @@ -162,15 +165,18 @@ SegmentInfoEntry::SegmentInfoEntry(const std::string& file_name, previous_segment_end_offset_(previous_segment_end_offset) {} std::string SegmentInfoEntry::ToString() { - std::string result = base::StringPrintf("#EXTINF:%.3f,\n", duration_); + std::string result = base::StringPrintf("#EXTINF:%.3f,", duration_); + if (use_byte_range_) { - result += "#EXT-X-BYTERANGE:" + base::Uint64ToString(segment_file_size_); + base::StringAppendF(&result, "\n#EXT-X-BYTERANGE:%" PRIu64, + segment_file_size_); if (previous_segment_end_offset_ + 1 != start_byte_offset_) { - result += "@" + base::Uint64ToString(start_byte_offset_); + base::StringAppendF(&result, "@%" PRIu64, start_byte_offset_); } - result += "\n"; } - result += file_name_ + "\n"; + + base::StringAppendF(&result, "\n%s", file_name_.c_str()); + return result; } @@ -212,32 +218,37 @@ EncryptionInfoEntry::EncryptionInfoEntry(MediaPlaylist::EncryptionMethod method, key_format_versions_(key_format_versions) {} std::string EncryptionInfoEntry::ToString() { + std::string tag_string; + Tag tag("#EXT-X-KEY", &tag_string); + std::string method_attribute; if (method_ == MediaPlaylist::EncryptionMethod::kSampleAes) { - method_attribute = "METHOD=SAMPLE-AES"; + tag.AddString("METHOD", "SAMPLE-AES"); } else if (method_ == MediaPlaylist::EncryptionMethod::kAes128) { - method_attribute = "METHOD=AES-128"; + tag.AddString("METHOD", "AES-128"); } else if (method_ == MediaPlaylist::EncryptionMethod::kSampleAesCenc) { - method_attribute = "METHOD=SAMPLE-AES-CTR"; + tag.AddString("METHOD", "SAMPLE-AES-CTR"); } else { DCHECK(method_ == MediaPlaylist::EncryptionMethod::kNone); - method_attribute = "METHOD=NONE"; + tag.AddString("METHOD", "NONE"); } - std::string ext_key = "#EXT-X-KEY:" + method_attribute + ",URI=\"" + url_ + - "\""; + + tag.AddQuotedString("URI", url_); + if (!key_id_.empty()) { - ext_key += ",KEYID=" + key_id_; + tag.AddString("KEYID", key_id_); } if (!iv_.empty()) { - ext_key += ",IV=" + iv_; + tag.AddString("IV", iv_); } if (!key_format_versions_.empty()) { - ext_key += ",KEYFORMATVERSIONS=\"" + key_format_versions_ + "\""; + tag.AddQuotedString("KEYFORMATVERSIONS", key_format_versions_); + } + if (!key_format_.empty()) { + tag.AddQuotedString("KEYFORMAT", key_format_); } - if (key_format_.empty()) - return ext_key + "\n"; - return ext_key + ",KEYFORMAT=\"" + key_format_ + "\"\n"; + return tag_string; } class DiscontinuityEntry : public HlsEntry { @@ -255,7 +266,7 @@ DiscontinuityEntry::DiscontinuityEntry() : HlsEntry(HlsEntry::EntryType::kExtDiscontinuity) {} std::string DiscontinuityEntry::ToString() { - return "#EXT-X-DISCONTINUITY\n"; + return "#EXT-X-DISCONTINUITY"; } class PlacementOpportunityEntry : public HlsEntry { @@ -274,7 +285,7 @@ PlacementOpportunityEntry::PlacementOpportunityEntry() : HlsEntry(HlsEntry::EntryType::kExtPlacementOpportunity) {} std::string PlacementOpportunityEntry::ToString() { - return "#EXT-X-PLACEMENT-OPPORTUNITY\n"; + return "#EXT-X-PLACEMENT-OPPORTUNITY"; } double LatestSegmentStartTime( @@ -424,15 +435,12 @@ bool MediaPlaylist::WriteToFile(const std::string& file_path) { SetTargetDuration(ceil(GetLongestSegmentDuration())); } - std::string header = CreatePlaylistHeader( + std::string content = CreatePlaylistHeader( media_info_, target_duration_, playlist_type_, stream_type_, media_sequence_number_, discontinuity_sequence_number_); - std::string body; for (const auto& entry : entries_) - body.append(entry->ToString()); - - std::string content = header + body; + base::StringAppendF(&content, "%s\n", entry->ToString().c_str()); if (playlist_type_ == HlsPlaylistType::kVod) { content += "#EXT-X-ENDLIST\n"; diff --git a/packager/hls/base/tag.cc b/packager/hls/base/tag.cc index 995038096c..4a34b064f9 100644 --- a/packager/hls/base/tag.cc +++ b/packager/hls/base/tag.cc @@ -32,12 +32,22 @@ void Tag::AddNumber(const std::string& key, uint64_t value) { base::StringAppendF(buffer_, "%s=%" PRIu64, key.c_str(), value); } -void Tag::AddResolution(const std::string& key, - uint32_t width, - uint32_t height) { +void Tag::AddNumberPair(const std::string& key, + uint64_t number1, + char separator, + uint64_t number2) { NextField(); - base::StringAppendF(buffer_, "%s=%" PRIu32 "x%" PRIu32, key.c_str(), width, - height); + base::StringAppendF(buffer_, "%s=%" PRIu64 "%c%" PRIu64, key.c_str(), number1, + separator, number2); +} + +void Tag::AddQuotedNumberPair(const std::string& key, + uint64_t number1, + char separator, + uint64_t number2) { + NextField(); + base::StringAppendF(buffer_, "%s=\"%" PRIu64 "%c%" PRIu64 "\"", key.c_str(), + number1, separator, number2); } void Tag::NextField() { diff --git a/packager/hls/base/tag.h b/packager/hls/base/tag.h index d3e7c4e17e..7319b65962 100644 --- a/packager/hls/base/tag.h +++ b/packager/hls/base/tag.h @@ -27,8 +27,17 @@ class Tag { /// Add a non-quoted numeric value to the argument list. void AddNumber(const std::string& key, uint64_t value); - /// Add a resolution value (AxB) to the argument list. - void AddResolution(const std::string& key, uint32_t width, uint32_t height); + /// Add a pair of numbers with a symbol separating them. + void AddNumberPair(const std::string& key, + uint64_t number1, + char separator, + uint64_t number2); + + /// Add a quoted pair of numbers with a symbol separating them. + void AddQuotedNumberPair(const std::string& key, + uint64_t number1, + char separator, + uint64_t number2); private: Tag(const Tag&) = delete;