Skip to content

Commit

Permalink
[DASH] Url construction that uses $RepresentationID$.
Browse files Browse the repository at this point in the history
  • Loading branch information
sr1990 committed Sep 26, 2020
1 parent 5b9fd40 commit 83cb6a2
Show file tree
Hide file tree
Showing 23 changed files with 203 additions and 38 deletions.
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;
};

} // 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

0 comments on commit 83cb6a2

Please sign in to comment.