[DASH] Url construction that uses $RepresentationID$.

This commit is contained in:
sr90 2020-09-25 18:57:47 -07:00
parent 5b9fd409a5
commit 83cb6a225e
23 changed files with 203 additions and 38 deletions

View File

@ -35,6 +35,7 @@ std::shared_ptr<Muxer> MuxerFactory::CreateMuxer(
options.output_file_name = stream.output; options.output_file_name = stream.output;
options.segment_template = stream.segment_template; options.segment_template = stream.segment_template;
options.bandwidth = stream.bandwidth; options.bandwidth = stream.bandwidth;
options.rep_id = stream.rep_id;
std::shared_ptr<Muxer> muxer; std::shared_ptr<Muxer> muxer;

View File

@ -35,6 +35,7 @@ enum FieldType {
kDashRolesField, kDashRolesField,
kDashOnlyField, kDashOnlyField,
kHlsOnlyField, kHlsOnlyField,
kRepIdField,
}; };
struct FieldNameToTypeMapping { struct FieldNameToTypeMapping {
@ -81,6 +82,7 @@ const FieldNameToTypeMapping kFieldNameTypeMappings[] = {
{"role", kDashRolesField}, {"role", kDashRolesField},
{"dash_only", kDashOnlyField}, {"dash_only", kDashOnlyField},
{"hls_only", kHlsOnlyField}, {"hls_only", kHlsOnlyField},
{"rep_id", kRepIdField},
}; };
FieldType GetFieldType(const std::string& field_name) { FieldType GetFieldType(const std::string& field_name) {
@ -236,6 +238,9 @@ base::Optional<StreamDescriptor> ParseStreamDescriptor(
} }
descriptor.hls_only = hls_only_value > 0; descriptor.hls_only = hls_only_value > 0;
break; break;
case kRepIdField:
descriptor.rep_id = iter->second;
break;
default: default:
LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first
<< "\")."; << "\").";

View File

@ -114,7 +114,7 @@ Status Muxer::ReinitializeMuxer(int64_t timestamp) {
// the subclasses. // the subclasses.
options_.output_file_name = options_.output_file_name =
GetSegmentName(output_file_template_, timestamp, output_file_index_++, GetSegmentName(output_file_template_, timestamp, output_file_index_++,
options_.bandwidth); options_.bandwidth, options_.rep_id);
} }
return InitializeMuxer(); return InitializeMuxer();
} }

View File

@ -45,6 +45,10 @@ struct MuxerOptions {
/// User-specified bit rate for the media stream. If zero, the muxer will /// User-specified bit rate for the media stream. If zero, the muxer will
/// attempt to estimate. /// attempt to estimate.
uint32_t bandwidth = 0; uint32_t bandwidth = 0;
/// Used as Representation id for template based url construction using
/// $RepresentationID$.
std::string rep_id;
}; };
} // namespace media } // namespace media

View File

@ -60,6 +60,8 @@ Status ValidateSegmentTemplate(const std::string& segment_template) {
bool has_number = false; bool has_number = false;
bool has_time = false; bool has_time = false;
bool has_representation = false;
// Every second substring in split output should be an identifier. // Every second substring in split output should be an identifier.
for (size_t i = 1; i < splits.size(); i += 2) { for (size_t i = 1; i < splits.size(); i += 2) {
// Each identifier may be suffixed, within the enclosing $ characters, // Each identifier may be suffixed, within the enclosing $ characters,
@ -76,9 +78,7 @@ Status ValidateSegmentTemplate(const std::string& segment_template) {
// TODO(kqyang): Support "RepresentationID". // TODO(kqyang): Support "RepresentationID".
if (identifier == "RepresentationID") { if (identifier == "RepresentationID") {
return Status( has_representation = true;
error::UNIMPLEMENTED,
"Segment template flag $RepresentationID$ is not supported yet.");
} else if (identifier == "Number") { } else if (identifier == "Number") {
has_number = true; has_number = true;
} else if (identifier == "Time") { } else if (identifier == "Time") {
@ -98,7 +98,7 @@ Status ValidateSegmentTemplate(const std::string& segment_template) {
error::INVALID_ARGUMENT, error::INVALID_ARGUMENT,
"In segment templates $Number$ and $Time$ should not co-exist."); "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, return Status(error::INVALID_ARGUMENT,
"In segment templates $Number$ or $Time$ should exist."); "In segment templates $Number$ or $Time$ should exist.");
} }
@ -111,7 +111,8 @@ Status ValidateSegmentTemplate(const std::string& segment_template) {
std::string GetSegmentName(const std::string& segment_template, std::string GetSegmentName(const std::string& segment_template,
uint64_t segment_start_time, uint64_t segment_start_time,
uint32_t segment_index, uint32_t segment_index,
uint32_t bandwidth) { uint32_t bandwidth,
std::string rep_id) {
DCHECK_EQ(Status::OK, ValidateSegmentTemplate(segment_template)); DCHECK_EQ(Status::OK, ValidateSegmentTemplate(segment_template));
std::vector<std::string> splits = base::SplitString( std::vector<std::string> splits = base::SplitString(
@ -135,7 +136,7 @@ std::string GetSegmentName(const std::string& segment_template,
size_t format_pos = splits[i].find('%'); size_t format_pos = splits[i].find('%');
std::string identifier = splits[i].substr(0, format_pos); std::string identifier = splits[i].substr(0, format_pos);
DCHECK(identifier == "Number" || identifier == "Time" || DCHECK(identifier == "Number" || identifier == "Time" ||
identifier == "Bandwidth"); identifier == "Bandwidth" || identifier == "RepresentationID");
std::string format_tag; std::string format_tag;
if (format_pos != std::string::npos) { if (format_pos != std::string::npos) {
@ -158,6 +159,8 @@ std::string GetSegmentName(const std::string& segment_template,
} else if (identifier == "Bandwidth") { } else if (identifier == "Bandwidth") {
segment_name += base::StringPrintf(format_tag.c_str(), segment_name += base::StringPrintf(format_tag.c_str(),
static_cast<uint64_t>(bandwidth)); static_cast<uint64_t>(bandwidth));
} else if (identifier == "RepresentationID") {
segment_name += rep_id;
} }
} }
return segment_name; return segment_name;

View File

@ -35,7 +35,8 @@ Status ValidateSegmentTemplate(const std::string& segment_template);
std::string GetSegmentName(const std::string& segment_template, std::string GetSegmentName(const std::string& segment_template,
uint64_t segment_start_time, uint64_t segment_start_time,
uint32_t segment_index, uint32_t segment_index,
uint32_t bandwidth); uint32_t bandwidth,
std::string rep_id = "");
} // namespace media } // namespace media
} // namespace shaka } // namespace shaka

View File

@ -39,7 +39,7 @@ TEST(MuxerUtilTest, ValidateSegmentTemplate) {
EXPECT_NE(Status::OK, ValidateSegmentTemplate("foo$Number$_$Time$loo")); EXPECT_NE(Status::OK, ValidateSegmentTemplate("foo$Number$_$Time$loo"));
// $RepresentationID$ not implemented yet. // $RepresentationID$ not implemented yet.
EXPECT_NE(Status::OK, ValidateSegmentTemplate("$RepresentationID$__$Time$")); EXPECT_EQ(Status::OK, ValidateSegmentTemplate("$RepresentationID$__$Time$"));
// Unknown identifier. // Unknown identifier.
EXPECT_NE(Status::OK, ValidateSegmentTemplate("$foo$$Time$")); EXPECT_NE(Status::OK, ValidateSegmentTemplate("$foo$$Time$"));

View File

@ -200,6 +200,9 @@ void SetMediaInfoMuxerOptions(const MuxerOptions& muxer_options,
if (!muxer_options.output_file_name.empty()) if (!muxer_options.output_file_name.empty())
media_info->set_init_segment_name(muxer_options.output_file_name); media_info->set_init_segment_name(muxer_options.output_file_name);
media_info->set_segment_template(muxer_options.segment_template); media_info->set_segment_template(muxer_options.segment_template);
if (!muxer_options.rep_id.empty()) {
media_info->set_rep_id(muxer_options.rep_id);
}
} }
} }

View File

@ -176,9 +176,9 @@ Status TsSegmenter::FinalizeSegment(uint64_t start_timestamp,
// be false. // be false.
if (!segment_started_) if (!segment_started_)
return Status::OK; return Status::OK;
std::string segment_path = std::string segment_path = GetSegmentName(
GetSegmentName(muxer_options_.segment_template, segment_start_timestamp_, muxer_options_.segment_template, segment_start_timestamp_,
segment_number_++, muxer_options_.bandwidth); segment_number_++, muxer_options_.bandwidth, muxer_options_.rep_id);
const int64_t file_size = segment_buffer_.Size(); const int64_t file_size = segment_buffer_.Size();
std::unique_ptr<File, FileCloser> segment_file; std::unique_ptr<File, FileCloser> segment_file;

View File

@ -110,9 +110,9 @@ Status MultiSegmentSegmenter::WriteSegment() {
options().output_file_name); options().output_file_name);
} }
} else { } else {
file_name = GetSegmentName(options().segment_template, file_name = GetSegmentName(
sidx()->earliest_presentation_time, options().segment_template, sidx()->earliest_presentation_time,
num_segments_++, options().bandwidth); num_segments_++, options().bandwidth, options().rep_id);
file.reset(File::Open(file_name.c_str(), "w")); file.reset(File::Open(file_name.c_str(), "w"));
if (!file) { if (!file) {
return Status(error::FILE_FAILURE, return Status(error::FILE_FAILURE,

View File

@ -79,7 +79,8 @@ Status PackedAudioWriter::FinalizeSegment(size_t stream_id,
options().segment_template.empty() options().segment_template.empty()
? options().output_file_name ? options().output_file_name
: GetSegmentName(options().segment_template, segment_timestamp, : 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. // Save |segment_size| as it will be cleared after writing.
const size_t segment_size = segmenter_->segment_buffer()->Size(); const size_t segment_size = segmenter_->segment_buffer()->Size();

View File

@ -34,7 +34,7 @@ Status MultiSegmentSegmenter::FinalizeSegment(uint64_t start_timestamp,
if (!is_subsegment) { if (!is_subsegment) {
std::string segment_name = std::string segment_name =
GetSegmentName(options().segment_template, start_timestamp, 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 // Close the file, which also does flushing, to make sure the file is
// written before manifest is updated. // written before manifest is updated.
@ -91,7 +91,7 @@ Status MultiSegmentSegmenter::NewSegment(uint64_t start_timestamp,
temp_file_name_ = temp_file_name_ =
"memory://" + GetSegmentName(options().segment_template, "memory://" + GetSegmentName(options().segment_template,
start_timestamp, num_segment_, start_timestamp, num_segment_,
options().bandwidth); options().bandwidth, options().rep_id);
writer_.reset(new MkvWriter); writer_.reset(new MkvWriter);
Status status = writer_->Open(temp_file_name_); Status status = writer_->Open(temp_file_name_);

View File

@ -90,8 +90,8 @@ Status WebVttTextOutputHandler::OnSegmentInfo(const SegmentInfo& info) {
const uint64_t duration = info.duration; const uint64_t duration = info.duration;
const uint32_t bandwidth = muxer_options_.bandwidth; const uint32_t bandwidth = muxer_options_.bandwidth;
const std::string filename = const std::string filename = GetSegmentName(segment_template, start, index,
GetSegmentName(segment_template, start, index, bandwidth); bandwidth, muxer_options_.rep_id);
// Write everything to the file before telling the manifest so that the // Write everything to the file before telling the manifest so that the
// file will exist on disk. // file will exist on disk.

View File

@ -336,6 +336,18 @@ xml::scoped_xml_ptr<xmlNode> AdaptationSet::GetXml() {
representation->SuppressOnce(Representation::kSuppressHeight); representation->SuppressOnce(Representation::kSuppressHeight);
if (suppress_representation_frame_rate) if (suppress_representation_frame_rate)
representation->SuppressOnce(Representation::kSuppressFrameRate); 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()); xml::scoped_xml_ptr<xmlNode> child(representation->GetXml());
if (!child || !adaptation_set.AddChild(std::move(child))) if (!child || !adaptation_set.AddChild(std::move(child)))
return xml::scoped_xml_ptr<xmlNode>(); return xml::scoped_xml_ptr<xmlNode>();
@ -409,7 +421,9 @@ void AdaptationSet::UpdateFromMediaInfo(const MediaInfo& media_info) {
AddPictureAspectRatio(video_info, &picture_aspect_ratio_); 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()) { if (media_info.has_video_info()) {
content_type_ = "video"; content_type_ = "video";
} else if (media_info.has_audio_info()) { } else if (media_info.has_audio_info()) {

View File

@ -317,6 +317,10 @@ class AdaptationSet {
// and HD videos in different AdaptationSets can share the same trick play // and HD videos in different AdaptationSets can share the same trick play
// stream. // stream.
std::vector<const AdaptationSet*> trick_play_references_; 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 } // namespace shaka

View File

@ -184,6 +184,7 @@ message MediaInfo {
// This value is not necessarily the same as the value passed to // This value is not necessarily the same as the value passed to
// MpdNotifier::NotifyNewSegment(). // MpdNotifier::NotifyNewSegment().
optional float segment_duration_seconds = 12 [deprecated = true]; optional float segment_duration_seconds = 12 [deprecated = true];
optional string rep_id = 23;
// END LIVE only. // END LIVE only.
// URL fields for the corresponding file_name fields above. // URL fields for the corresponding file_name fields above.

View File

@ -441,8 +441,20 @@ void MpdBuilder::MakePathsRelativeToMpd(const std::string& mpd_path,
MakePathRelative(media_info->init_segment_name(), mpd_dir)); MakePathRelative(media_info->init_segment_name(), mpd_dir));
} }
if (media_info->has_segment_template()) { if (media_info->has_segment_template()) {
media_info->set_segment_template_url( if (media_info->has_rep_id()) {
MakePathRelative(media_info->segment_template(), mpd_dir)); 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));
}
} }
} }
} }

View File

@ -95,7 +95,9 @@ Representation::Representation(
// TODO(kqyang): Need a better check. $Time is legitimate but not a // TODO(kqyang): Need a better check. $Time is legitimate but not a
// template. // template.
media_info.segment_template().find("$Time") == std::string::npos && 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( Representation::Representation(
const Representation& representation, const Representation& representation,
@ -212,6 +214,16 @@ const MediaInfo& Representation::GetMediaInfo() const {
return media_info_; 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 // Uses info in |media_info_| and |content_protection_elements_| to create a
// "Representation" node. // "Representation" node.
// MPD schema has strict ordering. The following must be done in order. // MPD schema has strict ordering. The following must be done in order.
@ -232,7 +244,12 @@ xml::scoped_xml_ptr<xmlNode> Representation::GetXml() {
xml::RepresentationXmlNode representation; xml::RepresentationXmlNode representation;
// Mandatory fields for 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); representation.SetIntegerAttribute("bandwidth", bandwidth);
if (!codecs_.empty()) if (!codecs_.empty())
representation.SetStringAttribute("codecs", codecs_); representation.SetStringAttribute("codecs", codecs_);
@ -269,6 +286,7 @@ xml::scoped_xml_ptr<xmlNode> Representation::GetXml() {
} }
if (HasLiveOnlyFields(media_info_) && if (HasLiveOnlyFields(media_info_) &&
!(output_suppression_flags_ & kSuppressSegmentTemplate) &&
!representation.AddLiveOnlyInfo(media_info_, segment_infos_, !representation.AddLiveOnlyInfo(media_info_, segment_infos_,
start_number_)) { start_number_)) {
LOG(ERROR) << "Failed to add Live info."; LOG(ERROR) << "Failed to add Live info.";
@ -452,9 +470,9 @@ void Representation::RemoveOldSegment(SegmentInfo* segment_info) {
if (mpd_options_.mpd_params.preserved_segments_outside_live_window == 0) if (mpd_options_.mpd_params.preserved_segments_outside_live_window == 0)
return; return;
segments_to_be_removed_.push_back( segments_to_be_removed_.push_back(media::GetSegmentName(
media::GetSegmentName(media_info_.segment_template(), segment_start_time, media_info_.segment_template(), segment_start_time, start_number_ - 1,
start_number_ - 1, media_info_.bandwidth())); media_info_.bandwidth(), media_info_.rep_id()));
while (segments_to_be_removed_.size() > while (segments_to_be_removed_.size() >
mpd_options_.mpd_params.preserved_segments_outside_live_window) { mpd_options_.mpd_params.preserved_segments_outside_live_window) {
VLOG(2) << "Deleting " << segments_to_be_removed_.front(); VLOG(2) << "Deleting " << segments_to_be_removed_.front();

View File

@ -57,6 +57,7 @@ class Representation {
kSuppressWidth = 1, kSuppressWidth = 1,
kSuppressHeight = 2, kSuppressHeight = 2,
kSuppressFrameRate = 4, kSuppressFrameRate = 4,
kSuppressSegmentTemplate = 8,
}; };
virtual ~Representation(); virtual ~Representation();
@ -118,6 +119,9 @@ class Representation {
/// @return Copy of <Representation>. /// @return Copy of <Representation>.
xml::scoped_xml_ptr<xmlNode> GetXml(); 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 /// By calling this methods, the next time GetXml() is
/// called, the corresponding attributes will not be set. /// called, the corresponding attributes will not be set.
/// For example, if SuppressOnce(kSuppressWidth) is called, then GetXml() will /// For example, if SuppressOnce(kSuppressWidth) is called, then GetXml() will
@ -244,6 +248,9 @@ class Representation {
// Segments with duration difference less than one frame duration are // Segments with duration difference less than one frame duration are
// considered to have the same duration. // considered to have the same duration.
uint32_t frame_duration_ = 0; uint32_t frame_duration_ = 0;
// When set to true, adds $RepresentationID$ in SegmentTemplate.
bool rep_id_set = false;
}; };
} // namespace shaka } // namespace shaka

View File

@ -197,6 +197,10 @@ void XmlNode::SetId(uint32_t id) {
SetIntegerAttribute("id", id); SetIntegerAttribute("id", id);
} }
void XmlNode::SetIdString(std::string id) {
SetStringAttribute("id", id);
}
void XmlNode::SetContent(const std::string& content) { void XmlNode::SetContent(const std::string& content) {
DCHECK(node_); DCHECK(node_);
xmlNodeSetContent(node_.get(), BAD_CAST content.c_str()); xmlNodeSetContent(node_.get(), BAD_CAST content.c_str());
@ -404,6 +408,77 @@ bool RepresentationXmlNode::AddVODOnlyInfo(const MediaInfo& media_info) {
return true; return true;
} }
scoped_xml_ptr<xmlNode> RepresentationXmlNode::GetLiveOnlyInfo(
const MediaInfo& media_info,
const std::list<SegmentInfo>& segment_infos,
uint32_t start_number) {
XmlNode segment_template("SegmentTemplate");
if (media_info.has_reference_time_scale()) {
segment_template.SetIntegerAttribute("timescale",
media_info.reference_time_scale());
}
if (media_info.has_presentation_time_offset()) {
segment_template.SetIntegerAttribute("presentationTimeOffset",
media_info.presentation_time_offset());
}
if (media_info.has_init_segment_url()) {
if (media_info.has_rep_id() &&
media_info.init_segment_url().find(media_info.rep_id()) !=
std::string::npos) {
std::string temp = media_info.init_segment_url();
temp.replace(media_info.init_segment_url().find(media_info.rep_id()),
media_info.rep_id().length(), "$RepresentationID$");
segment_template.SetStringAttribute("initialization", temp);
} else {
segment_template.SetStringAttribute("initialization",
media_info.init_segment_url());
}
}
if (media_info.has_segment_template_url()) {
if (media_info.has_rep_id() &&
media_info.segment_template_url().find(media_info.rep_id()) !=
std::string::npos) {
std::string temp = media_info.segment_template_url();
temp.replace(media_info.segment_template_url().find(media_info.rep_id()),
media_info.rep_id().length(), "$RepresentationID$");
segment_template.SetStringAttribute("media", temp);
} else {
segment_template.SetStringAttribute("media",
media_info.segment_template_url());
}
segment_template.SetIntegerAttribute("startNumber", start_number);
}
if (!segment_infos.empty()) {
// Don't use SegmentTimeline if all segments except the last one are of
// the same duration.
if (IsTimelineConstantDuration(segment_infos, start_number)) {
segment_template.SetIntegerAttribute("duration",
segment_infos.front().duration);
if (FLAGS_dash_add_last_segment_number_when_needed) {
uint32_t last_segment_number = start_number - 1;
for (const auto& segment_info_element : segment_infos)
last_segment_number += segment_info_element.repeat + 1;
AddSupplementalProperty(
"http://dashif.org/guidelines/last-segment-number",
std::to_string(last_segment_number));
}
} else {
XmlNode segment_timeline("SegmentTimeline");
CHECK(PopulateSegmentTimeline(segment_infos, &segment_timeline));
CHECK(segment_template.AddChild(segment_timeline.PassScopedPtr()));
}
}
return segment_template.PassScopedPtr();
}
bool RepresentationXmlNode::AddLiveOnlyInfo( bool RepresentationXmlNode::AddLiveOnlyInfo(
const MediaInfo& media_info, const MediaInfo& media_info,
const std::list<SegmentInfo>& segment_infos, const std::list<SegmentInfo>& segment_infos,
@ -483,15 +558,15 @@ bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) {
audio_channel_config_value = base::UintToString(ec3_channel_mpeg_value); audio_channel_config_value = base::UintToString(ec3_channel_mpeg_value);
audio_channel_config_scheme = "urn:mpeg:mpegB:cicp:ChannelConfiguration"; audio_channel_config_scheme = "urn:mpeg:mpegB:cicp:ChannelConfiguration";
} }
bool ret = AddDescriptor("AudioChannelConfiguration", bool ret =
audio_channel_config_scheme, AddDescriptor("AudioChannelConfiguration", audio_channel_config_scheme,
audio_channel_config_value); audio_channel_config_value);
// Dolby Digital Plus JOC descriptor. Spec: ETSI TS 103 420 v1.2.1 // Dolby Digital Plus JOC descriptor. Spec: ETSI TS 103 420 v1.2.1
// Backwards-compatible object audio carriage using Enhanced AC-3 Standard // Backwards-compatible object audio carriage using Enhanced AC-3 Standard
// D.2.2. // D.2.2.
if (codec_data.ec3_joc_complexity() != 0) { if (codec_data.ec3_joc_complexity() != 0) {
std::string ec3_joc_complexity = std::string ec3_joc_complexity =
base::UintToString(codec_data.ec3_joc_complexity()); base::UintToString(codec_data.ec3_joc_complexity());
ret &= AddDescriptor("SupplementalProperty", ret &= AddDescriptor("SupplementalProperty",
"tag:dolby.com,2018:dash:EC3_ExtensionType:2018", "tag:dolby.com,2018:dash:EC3_ExtensionType:2018",
"JOC"); "JOC");

View File

@ -66,6 +66,10 @@ class XmlNode {
/// @param id is the ID for this element. /// @param id is the ID for this element.
void SetId(uint32_t id); void SetId(uint32_t id);
/// Sets 'id=rep_id' attribute.
/// @param id is the ID for this element.
void SetIdString(std::string id);
/// Set the contents of an XML element using a string. /// Set the contents of an XML element using a string.
/// This cannot set child elements because <> will become &lt; and &rt; /// This cannot set child elements because <> will become &lt; and &rt;
/// This should be used to set the text for the element, e.g. setting /// This should be used to set the text for the element, e.g. setting
@ -188,6 +192,14 @@ class RepresentationXmlNode : public RepresentationBaseXmlNode {
const std::list<SegmentInfo>& segment_infos, const std::list<SegmentInfo>& segment_infos,
uint32_t start_number); uint32_t start_number);
/// @param segment_infos is a set of SegmentInfos. This method assumes that
/// SegmentInfos are sorted by its start time.
/// @return SegmentTemplate node.
scoped_xml_ptr<xmlNode> GetLiveOnlyInfo(
const MediaInfo& media_info,
const std::list<SegmentInfo>& segment_infos,
uint32_t start_number);
private: private:
// Add AudioChannelConfiguration element. Note that it is a required element // Add AudioChannelConfiguration element. Note that it is a required element
// for audio Representations. // for audio Representations.

View File

@ -316,7 +316,8 @@ Status ValidateParams(const PackagingParams& packaging_params,
} }
if (!descriptor.output.empty()) { if (!descriptor.output.empty()) {
if (outputs.find(descriptor.output) != outputs.end()) { if (descriptor.output.find("$RepresentationID$") == std::string::npos &&
outputs.find(descriptor.output) != outputs.end()) {
return Status( return Status(
error::INVALID_ARGUMENT, error::INVALID_ARGUMENT,
"Seeing duplicated outputs '" + descriptor.output + "Seeing duplicated outputs '" + descriptor.output +
@ -325,8 +326,10 @@ Status ValidateParams(const PackagingParams& packaging_params,
outputs.insert(descriptor.output); outputs.insert(descriptor.output);
} }
if (!descriptor.segment_template.empty()) { if (!descriptor.segment_template.empty()) {
if (segment_templates.find(descriptor.segment_template) != if (descriptor.segment_template.find("$RepresentationID$") ==
segment_templates.end()) { std::string::npos &&
segment_templates.find(descriptor.segment_template) !=
segment_templates.end()) {
return Status(error::INVALID_ARGUMENT, return Status(error::INVALID_ARGUMENT,
"Seeing duplicated segment templates '" + "Seeing duplicated segment templates '" +
descriptor.segment_template + descriptor.segment_template +

View File

@ -133,6 +133,8 @@ struct StreamDescriptor {
bool dash_only = false; bool dash_only = false;
/// Set to true to indicate that the stream is for hls only. /// Set to true to indicate that the stream is for hls only.
bool hls_only = false; bool hls_only = false;
std::string rep_id;
}; };
class SHAKA_EXPORT Packager { class SHAKA_EXPORT Packager {
@ -144,9 +146,8 @@ class SHAKA_EXPORT Packager {
/// @param packaging_params contains the packaging parameters. /// @param packaging_params contains the packaging parameters.
/// @param stream_descriptors a list of stream descriptors. /// @param stream_descriptors a list of stream descriptors.
/// @return OK on success, an appropriate error code on failure. /// @return OK on success, an appropriate error code on failure.
Status Initialize( Status Initialize(const PackagingParams& packaging_params,
const PackagingParams& packaging_params, const std::vector<StreamDescriptor>& stream_descriptors);
const std::vector<StreamDescriptor>& stream_descriptors);
/// Run the pipeline to completion (or failed / been cancelled). Note /// Run the pipeline to completion (or failed / been cancelled). Note
/// that it blocks until completion. /// that it blocks until completion.