[DASH] Url construction that uses $RepresentationID$.
This commit is contained in:
parent
5b9fd409a5
commit
83cb6a225e
|
@ -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;
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ enum FieldType {
|
|||
kDashRolesField,
|
||||
kDashOnlyField,
|
||||
kHlsOnlyField,
|
||||
kRepIdField,
|
||||
};
|
||||
|
||||
struct FieldNameToTypeMapping {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
<< "\").";
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
@ -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") {
|
||||
|
@ -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.");
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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$"));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
@ -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_);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>();
|
||||
|
@ -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()) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -441,11 +441,23 @@ void MpdBuilder::MakePathsRelativeToMpd(const std::string& mpd_path,
|
|||
MakePathRelative(media_info->init_segment_name(), mpd_dir));
|
||||
}
|
||||
if (media_info->has_segment_template()) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace shaka
|
||||
|
|
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -232,7 +244,12 @@ xml::scoped_xml_ptr<xmlNode> Representation::GetXml() {
|
|||
|
||||
xml::RepresentationXmlNode representation;
|
||||
// Mandatory fields for Representation.
|
||||
|
||||
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_);
|
||||
|
@ -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.";
|
||||
|
@ -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();
|
||||
|
|
|
@ -57,6 +57,7 @@ class Representation {
|
|||
kSuppressWidth = 1,
|
||||
kSuppressHeight = 2,
|
||||
kSuppressFrameRate = 4,
|
||||
kSuppressSegmentTemplate = 8,
|
||||
};
|
||||
|
||||
virtual ~Representation();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -197,6 +197,10 @@ void XmlNode::SetId(uint32_t id) {
|
|||
SetIntegerAttribute("id", id);
|
||||
}
|
||||
|
||||
void XmlNode::SetIdString(std::string id) {
|
||||
SetStringAttribute("id", id);
|
||||
}
|
||||
|
||||
void XmlNode::SetContent(const std::string& content) {
|
||||
DCHECK(node_);
|
||||
xmlNodeSetContent(node_.get(), BAD_CAST content.c_str());
|
||||
|
@ -404,6 +408,77 @@ bool RepresentationXmlNode::AddVODOnlyInfo(const MediaInfo& media_info) {
|
|||
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(
|
||||
const MediaInfo& media_info,
|
||||
const std::list<SegmentInfo>& segment_infos,
|
||||
|
@ -483,8 +558,8 @@ bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) {
|
|||
audio_channel_config_value = base::UintToString(ec3_channel_mpeg_value);
|
||||
audio_channel_config_scheme = "urn:mpeg:mpegB:cicp:ChannelConfiguration";
|
||||
}
|
||||
bool ret = AddDescriptor("AudioChannelConfiguration",
|
||||
audio_channel_config_scheme,
|
||||
bool ret =
|
||||
AddDescriptor("AudioChannelConfiguration", audio_channel_config_scheme,
|
||||
audio_channel_config_value);
|
||||
// Dolby Digital Plus JOC descriptor. Spec: ETSI TS 103 420 v1.2.1
|
||||
// Backwards-compatible object audio carriage using Enhanced AC-3 Standard
|
||||
|
|
|
@ -66,6 +66,10 @@ class XmlNode {
|
|||
/// @param id is the ID for this element.
|
||||
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.
|
||||
/// This cannot set child elements because <> will become < and &rt;
|
||||
/// 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,
|
||||
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:
|
||||
// Add AudioChannelConfiguration element. Note that it is a required element
|
||||
// for audio Representations.
|
||||
|
|
|
@ -316,7 +316,8 @@ Status ValidateParams(const PackagingParams& packaging_params,
|
|||
}
|
||||
|
||||
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(
|
||||
error::INVALID_ARGUMENT,
|
||||
"Seeing duplicated outputs '" + descriptor.output +
|
||||
|
@ -325,7 +326,9 @@ Status ValidateParams(const PackagingParams& packaging_params,
|
|||
outputs.insert(descriptor.output);
|
||||
}
|
||||
if (!descriptor.segment_template.empty()) {
|
||||
if (segment_templates.find(descriptor.segment_template) !=
|
||||
if (descriptor.segment_template.find("$RepresentationID$") ==
|
||||
std::string::npos &&
|
||||
segment_templates.find(descriptor.segment_template) !=
|
||||
segment_templates.end()) {
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
"Seeing duplicated segment templates '" +
|
||||
|
|
|
@ -133,6 +133,8 @@ struct StreamDescriptor {
|
|||
bool dash_only = false;
|
||||
/// Set to true to indicate that the stream is for hls only.
|
||||
bool hls_only = false;
|
||||
|
||||
std::string rep_id;
|
||||
};
|
||||
|
||||
class SHAKA_EXPORT Packager {
|
||||
|
@ -144,8 +146,7 @@ class SHAKA_EXPORT Packager {
|
|||
/// @param packaging_params contains the packaging parameters.
|
||||
/// @param stream_descriptors a list of stream descriptors.
|
||||
/// @return OK on success, an appropriate error code on failure.
|
||||
Status Initialize(
|
||||
const PackagingParams& packaging_params,
|
||||
Status Initialize(const PackagingParams& packaging_params,
|
||||
const std::vector<StreamDescriptor>& stream_descriptors);
|
||||
|
||||
/// Run the pipeline to completion (or failed / been cancelled). Note
|
||||
|
|
Loading…
Reference in New Issue