Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DASH] Template based url construction with RepresentationID. #848

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packager/app/muxer_factory.cc
Expand Up @@ -35,6 +35,7 @@ std::shared_ptr<Muxer> MuxerFactory::CreateMuxer(
options.output_file_name = stream.output;
options.segment_template = stream.segment_template;
options.bandwidth = stream.bandwidth;
options.rep_id = stream.rep_id;

std::shared_ptr<Muxer> muxer;

Expand Down
5 changes: 5 additions & 0 deletions packager/app/stream_descriptor.cc
Expand Up @@ -35,6 +35,7 @@ enum FieldType {
kDashRolesField,
kDashOnlyField,
kHlsOnlyField,
kRepIdField,
};

struct FieldNameToTypeMapping {
Expand Down Expand Up @@ -81,6 +82,7 @@ const FieldNameToTypeMapping kFieldNameTypeMappings[] = {
{"role", kDashRolesField},
{"dash_only", kDashOnlyField},
{"hls_only", kHlsOnlyField},
{"rep_id", kRepIdField},
};

FieldType GetFieldType(const std::string& field_name) {
Expand Down Expand Up @@ -236,6 +238,9 @@ base::Optional<StreamDescriptor> ParseStreamDescriptor(
}
descriptor.hls_only = hls_only_value > 0;
break;
case kRepIdField:
descriptor.rep_id = iter->second;
break;
default:
LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first
<< "\").";
Expand Down
2 changes: 1 addition & 1 deletion packager/media/base/muxer.cc
Expand Up @@ -114,7 +114,7 @@ Status Muxer::ReinitializeMuxer(int64_t timestamp) {
// the subclasses.
options_.output_file_name =
GetSegmentName(output_file_template_, timestamp, output_file_index_++,
options_.bandwidth);
options_.bandwidth, options_.rep_id);
}
return InitializeMuxer();
}
Expand Down
4 changes: 4 additions & 0 deletions packager/media/base/muxer_options.h
Expand Up @@ -45,6 +45,10 @@ struct MuxerOptions {
/// User-specified bit rate for the media stream. If zero, the muxer will
/// attempt to estimate.
uint32_t bandwidth = 0;

/// Used as Representation id for template based url construction using
/// $RepresentationID$.
std::string rep_id;
Copy link
Contributor Author

@sr1990 sr1990 Jun 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be added as a muxer option. Will update the PR soon.

};

} // namespace media
Expand Down
15 changes: 9 additions & 6 deletions packager/media/base/muxer_util.cc
Expand Up @@ -60,6 +60,8 @@ Status ValidateSegmentTemplate(const std::string& segment_template) {

bool has_number = false;
bool has_time = false;
bool has_representation = false;

// Every second substring in split output should be an identifier.
for (size_t i = 1; i < splits.size(); i += 2) {
// Each identifier may be suffixed, within the enclosing ‘$’ characters,
Expand All @@ -76,9 +78,7 @@ Status ValidateSegmentTemplate(const std::string& segment_template) {

// TODO(kqyang): Support "RepresentationID".
if (identifier == "RepresentationID") {
return Status(
error::UNIMPLEMENTED,
"Segment template flag $RepresentationID$ is not supported yet.");
has_representation = true;
} else if (identifier == "Number") {
has_number = true;
} else if (identifier == "Time") {
Expand All @@ -98,7 +98,7 @@ Status ValidateSegmentTemplate(const std::string& segment_template) {
error::INVALID_ARGUMENT,
"In segment templates $Number$ and $Time$ should not co-exist.");
}
if (!has_number && !has_time) {
if (!has_number && !has_time && !has_representation) {
return Status(error::INVALID_ARGUMENT,
"In segment templates $Number$ or $Time$ should exist.");
}
Expand All @@ -111,7 +111,8 @@ Status ValidateSegmentTemplate(const std::string& segment_template) {
std::string GetSegmentName(const std::string& segment_template,
uint64_t segment_start_time,
uint32_t segment_index,
uint32_t bandwidth) {
uint32_t bandwidth,
std::string rep_id) {
DCHECK_EQ(Status::OK, ValidateSegmentTemplate(segment_template));

std::vector<std::string> splits = base::SplitString(
Expand All @@ -135,7 +136,7 @@ std::string GetSegmentName(const std::string& segment_template,
size_t format_pos = splits[i].find('%');
std::string identifier = splits[i].substr(0, format_pos);
DCHECK(identifier == "Number" || identifier == "Time" ||
identifier == "Bandwidth");
identifier == "Bandwidth" || identifier == "RepresentationID");

std::string format_tag;
if (format_pos != std::string::npos) {
Expand All @@ -158,6 +159,8 @@ std::string GetSegmentName(const std::string& segment_template,
} else if (identifier == "Bandwidth") {
segment_name += base::StringPrintf(format_tag.c_str(),
static_cast<uint64_t>(bandwidth));
} else if (identifier == "RepresentationID") {
segment_name += rep_id;
}
}
return segment_name;
Expand Down
3 changes: 2 additions & 1 deletion packager/media/base/muxer_util.h
Expand Up @@ -35,7 +35,8 @@ Status ValidateSegmentTemplate(const std::string& segment_template);
std::string GetSegmentName(const std::string& segment_template,
uint64_t segment_start_time,
uint32_t segment_index,
uint32_t bandwidth);
uint32_t bandwidth,
std::string rep_id = "");

} // namespace media
} // namespace shaka
Expand Down
2 changes: 1 addition & 1 deletion packager/media/base/muxer_util_unittest.cc
Expand Up @@ -39,7 +39,7 @@ TEST(MuxerUtilTest, ValidateSegmentTemplate) {
EXPECT_NE(Status::OK, ValidateSegmentTemplate("foo$Number$_$Time$loo"));

// $RepresentationID$ not implemented yet.
EXPECT_NE(Status::OK, ValidateSegmentTemplate("$RepresentationID$__$Time$"));
EXPECT_EQ(Status::OK, ValidateSegmentTemplate("$RepresentationID$__$Time$"));

// Unknown identifier.
EXPECT_NE(Status::OK, ValidateSegmentTemplate("$foo$$Time$"));
Expand Down
3 changes: 3 additions & 0 deletions packager/media/event/muxer_listener_internal.cc
Expand Up @@ -200,6 +200,9 @@ void SetMediaInfoMuxerOptions(const MuxerOptions& muxer_options,
if (!muxer_options.output_file_name.empty())
media_info->set_init_segment_name(muxer_options.output_file_name);
media_info->set_segment_template(muxer_options.segment_template);
if (!muxer_options.rep_id.empty()) {
media_info->set_rep_id(muxer_options.rep_id);
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions packager/media/formats/mp2t/ts_segmenter.cc
Expand Up @@ -176,9 +176,9 @@ Status TsSegmenter::FinalizeSegment(uint64_t start_timestamp,
// be false.
if (!segment_started_)
return Status::OK;
std::string segment_path =
GetSegmentName(muxer_options_.segment_template, segment_start_timestamp_,
segment_number_++, muxer_options_.bandwidth);
std::string segment_path = GetSegmentName(
muxer_options_.segment_template, segment_start_timestamp_,
segment_number_++, muxer_options_.bandwidth, muxer_options_.rep_id);

const int64_t file_size = segment_buffer_.Size();
std::unique_ptr<File, FileCloser> segment_file;
Expand Down
6 changes: 3 additions & 3 deletions packager/media/formats/mp4/multi_segment_segmenter.cc
Expand Up @@ -110,9 +110,9 @@ Status MultiSegmentSegmenter::WriteSegment() {
options().output_file_name);
}
} else {
file_name = GetSegmentName(options().segment_template,
sidx()->earliest_presentation_time,
num_segments_++, options().bandwidth);
file_name = GetSegmentName(
options().segment_template, sidx()->earliest_presentation_time,
num_segments_++, options().bandwidth, options().rep_id);
file.reset(File::Open(file_name.c_str(), "w"));
if (!file) {
return Status(error::FILE_FAILURE,
Expand Down
3 changes: 2 additions & 1 deletion packager/media/formats/packed_audio/packed_audio_writer.cc
Expand Up @@ -79,7 +79,8 @@ Status PackedAudioWriter::FinalizeSegment(size_t stream_id,
options().segment_template.empty()
? options().output_file_name
: GetSegmentName(options().segment_template, segment_timestamp,
segment_number_++, options().bandwidth);
segment_number_++, options().bandwidth,
options().rep_id);

// Save |segment_size| as it will be cleared after writing.
const size_t segment_size = segmenter_->segment_buffer()->Size();
Expand Down
4 changes: 2 additions & 2 deletions packager/media/formats/webm/multi_segment_segmenter.cc
Expand Up @@ -34,7 +34,7 @@ Status MultiSegmentSegmenter::FinalizeSegment(uint64_t start_timestamp,
if (!is_subsegment) {
std::string segment_name =
GetSegmentName(options().segment_template, start_timestamp,
num_segment_, options().bandwidth);
num_segment_, options().bandwidth, options().rep_id);

// Close the file, which also does flushing, to make sure the file is
// written before manifest is updated.
Expand Down Expand Up @@ -91,7 +91,7 @@ Status MultiSegmentSegmenter::NewSegment(uint64_t start_timestamp,
temp_file_name_ =
"memory://" + GetSegmentName(options().segment_template,
start_timestamp, num_segment_,
options().bandwidth);
options().bandwidth, options().rep_id);

writer_.reset(new MkvWriter);
Status status = writer_->Open(temp_file_name_);
Expand Down
4 changes: 2 additions & 2 deletions packager/media/formats/webvtt/webvtt_text_output_handler.cc
Expand Up @@ -90,8 +90,8 @@ Status WebVttTextOutputHandler::OnSegmentInfo(const SegmentInfo& info) {
const uint64_t duration = info.duration;
const uint32_t bandwidth = muxer_options_.bandwidth;

const std::string filename =
GetSegmentName(segment_template, start, index, bandwidth);
const std::string filename = GetSegmentName(segment_template, start, index,
bandwidth, muxer_options_.rep_id);

// Write everything to the file before telling the manifest so that the
// file will exist on disk.
Expand Down
16 changes: 15 additions & 1 deletion packager/mpd/base/adaptation_set.cc
Expand Up @@ -336,6 +336,18 @@ xml::scoped_xml_ptr<xmlNode> AdaptationSet::GetXml() {
representation->SuppressOnce(Representation::kSuppressHeight);
if (suppress_representation_frame_rate)
representation->SuppressOnce(Representation::kSuppressFrameRate);

if (include_segment_template_in_adaptation_set)
representation->SuppressOnce(Representation::kSuppressSegmentTemplate);

if (!adaptation_set.GetRawPtr()->children &&
include_segment_template_in_adaptation_set) {
xml::scoped_xml_ptr<xmlNode> live_child(
representation->GetLiveOnlyInfo());
if (!live_child || !adaptation_set.AddChild(std::move(live_child)))
return xml::scoped_xml_ptr<xmlNode>();
}

xml::scoped_xml_ptr<xmlNode> child(representation->GetXml());
if (!child || !adaptation_set.AddChild(std::move(child)))
return xml::scoped_xml_ptr<xmlNode>();
Expand Down Expand Up @@ -409,7 +421,9 @@ void AdaptationSet::UpdateFromMediaInfo(const MediaInfo& media_info) {

AddPictureAspectRatio(video_info, &picture_aspect_ratio_);
}

if (media_info.has_rep_id()) {
include_segment_template_in_adaptation_set = true;
}
if (media_info.has_video_info()) {
content_type_ = "video";
} else if (media_info.has_audio_info()) {
Expand Down
4 changes: 4 additions & 0 deletions packager/mpd/base/adaptation_set.h
Expand Up @@ -317,6 +317,10 @@ class AdaptationSet {
// and HD videos in different AdaptationSets can share the same trick play
// stream.
std::vector<const AdaptationSet*> trick_play_references_;

// Set to true if SegmentTemplate needs to be added to AdaptationSet,
// instead of Representation.
bool include_segment_template_in_adaptation_set = false;
};

} // namespace shaka
Expand Down
1 change: 1 addition & 0 deletions packager/mpd/base/media_info.proto
Expand Up @@ -184,6 +184,7 @@ message MediaInfo {
// This value is not necessarily the same as the value passed to
// MpdNotifier::NotifyNewSegment().
optional float segment_duration_seconds = 12 [deprecated = true];
optional string rep_id = 23;
// END LIVE only.

// URL fields for the corresponding file_name fields above.
Expand Down
16 changes: 14 additions & 2 deletions packager/mpd/base/mpd_builder.cc
Expand Up @@ -441,8 +441,20 @@ void MpdBuilder::MakePathsRelativeToMpd(const std::string& mpd_path,
MakePathRelative(media_info->init_segment_name(), mpd_dir));
}
if (media_info->has_segment_template()) {
media_info->set_segment_template_url(
MakePathRelative(media_info->segment_template(), mpd_dir));
if (media_info->has_rep_id()) {
if (media_info->segment_template().find("$RepresentationID") !=
std::string::npos) {
std::string temp = media_info->segment_template();
temp.replace(
media_info->segment_template().find("$RepresentationID"), 18,
media_info->rep_id());
media_info->set_segment_template_url(
MakePathRelative(temp, mpd_dir));
}
} else {
media_info->set_segment_template_url(
MakePathRelative(media_info->segment_template(), mpd_dir));
}
}
}
}
Expand Down
28 changes: 23 additions & 5 deletions packager/mpd/base/representation.cc
Expand Up @@ -95,7 +95,9 @@ Representation::Representation(
// TODO(kqyang): Need a better check. $Time is legitimate but not a
// template.
media_info.segment_template().find("$Time") == std::string::npos &&
mpd_options_.mpd_params.allow_approximate_segment_timeline) {}
mpd_options_.mpd_params.allow_approximate_segment_timeline),
rep_id_set((media_info.segment_template().find("$RepresentationID$") !=
std::string::npos)) {}

Representation::Representation(
const Representation& representation,
Expand Down Expand Up @@ -212,6 +214,16 @@ const MediaInfo& Representation::GetMediaInfo() const {
return media_info_;
}

xml::scoped_xml_ptr<xmlNode> Representation::GetLiveOnlyInfo() {
xml::RepresentationXmlNode representation;

if (HasLiveOnlyFields(media_info_)) {
return representation.GetLiveOnlyInfo(media_info_, segment_infos_,
start_number_);
}
return xml::scoped_xml_ptr<xmlNode>();
}

// Uses info in |media_info_| and |content_protection_elements_| to create a
// "Representation" node.
// MPD schema has strict ordering. The following must be done in order.
Expand All @@ -232,7 +244,12 @@ xml::scoped_xml_ptr<xmlNode> Representation::GetXml() {

xml::RepresentationXmlNode representation;
// Mandatory fields for Representation.
representation.SetId(id_);

if (rep_id_set) {
representation.SetIdString(media_info_.rep_id());
} else {
representation.SetId(id_);
}
representation.SetIntegerAttribute("bandwidth", bandwidth);
if (!codecs_.empty())
representation.SetStringAttribute("codecs", codecs_);
Expand Down Expand Up @@ -269,6 +286,7 @@ xml::scoped_xml_ptr<xmlNode> Representation::GetXml() {
}

if (HasLiveOnlyFields(media_info_) &&
!(output_suppression_flags_ & kSuppressSegmentTemplate) &&
!representation.AddLiveOnlyInfo(media_info_, segment_infos_,
start_number_)) {
LOG(ERROR) << "Failed to add Live info.";
Expand Down Expand Up @@ -452,9 +470,9 @@ void Representation::RemoveOldSegment(SegmentInfo* segment_info) {
if (mpd_options_.mpd_params.preserved_segments_outside_live_window == 0)
return;

segments_to_be_removed_.push_back(
media::GetSegmentName(media_info_.segment_template(), segment_start_time,
start_number_ - 1, media_info_.bandwidth()));
segments_to_be_removed_.push_back(media::GetSegmentName(
media_info_.segment_template(), segment_start_time, start_number_ - 1,
media_info_.bandwidth(), media_info_.rep_id()));
while (segments_to_be_removed_.size() >
mpd_options_.mpd_params.preserved_segments_outside_live_window) {
VLOG(2) << "Deleting " << segments_to_be_removed_.front();
Expand Down
7 changes: 7 additions & 0 deletions packager/mpd/base/representation.h
Expand Up @@ -57,6 +57,7 @@ class Representation {
kSuppressWidth = 1,
kSuppressHeight = 2,
kSuppressFrameRate = 4,
kSuppressSegmentTemplate = 8,
};

virtual ~Representation();
Expand Down Expand Up @@ -118,6 +119,9 @@ class Representation {
/// @return Copy of <Representation>.
xml::scoped_xml_ptr<xmlNode> GetXml();

/// @return SegmentTemplate xmlNode if live information is present.
xml::scoped_xml_ptr<xmlNode> GetLiveOnlyInfo();

/// By calling this methods, the next time GetXml() is
/// called, the corresponding attributes will not be set.
/// For example, if SuppressOnce(kSuppressWidth) is called, then GetXml() will
Expand Down Expand Up @@ -244,6 +248,9 @@ class Representation {
// Segments with duration difference less than one frame duration are
// considered to have the same duration.
uint32_t frame_duration_ = 0;

// When set to true, adds $RepresentationID$ in SegmentTemplate.
bool rep_id_set = false;
};

} // namespace shaka
Expand Down