Add mimeType to Representation and use float for duration
Work on some TODOs. Mandatory mimeType field was missing from Representation. All duration in the MPD is now float. Change-Id: I85b8511bc1c43759d80f831bee371c398895cf39
This commit is contained in:
parent
4723ef777d
commit
a84d6a7d09
|
@ -19,6 +19,27 @@ using xml::AdaptationSetXmlNode;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
std::string GetMimeType(
|
||||||
|
const std::string& prefix,
|
||||||
|
MediaInfo::ContainerType container_type) {
|
||||||
|
switch (container_type) {
|
||||||
|
case MediaInfo::CONTAINER_MP4:
|
||||||
|
return prefix + "/mp4";
|
||||||
|
case MediaInfo::CONTAINER_MPEG2_TS:
|
||||||
|
// TODO(rkuroiwa): Find out whether uppercase or lowercase should be used
|
||||||
|
// for mp2t. DASH MPD spec uses lowercase but RFC3555 says uppercase.
|
||||||
|
return prefix + "/MP2T";
|
||||||
|
case MediaInfo::CONTAINER_WEBM:
|
||||||
|
return prefix + "/webm";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsupported container types should be rejected/handled by the caller.
|
||||||
|
NOTREACHED() << "Unrecognized container type: " << container_type;
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
void AddMpdNameSpaceInfo(XmlNode* mpd) {
|
void AddMpdNameSpaceInfo(XmlNode* mpd) {
|
||||||
DCHECK(mpd);
|
DCHECK(mpd);
|
||||||
|
|
||||||
|
@ -101,8 +122,8 @@ xmlDocPtr MpdBuilder::GenerateMpd() {
|
||||||
XmlNode mpd("MPD");
|
XmlNode mpd("MPD");
|
||||||
AddMpdNameSpaceInfo(&mpd);
|
AddMpdNameSpaceInfo(&mpd);
|
||||||
|
|
||||||
// Currently set to 2. Does this need calculation?
|
// TODO(rkuroiwa): Currently set to 2. Does this need calculation?
|
||||||
const int kMinBufferTime = 2;
|
const float kMinBufferTime = 2.0f;
|
||||||
mpd.SetStringAttribute("minBufferTime", SecondsToXmlDuration(kMinBufferTime));
|
mpd.SetStringAttribute("minBufferTime", SecondsToXmlDuration(kMinBufferTime));
|
||||||
|
|
||||||
// Iterate thru AdaptationSets and add them to one big Period element.
|
// Iterate thru AdaptationSets and add them to one big Period element.
|
||||||
|
@ -159,7 +180,7 @@ void MpdBuilder::AddStaticMpdInfo(XmlNode* mpd_node) {
|
||||||
SecondsToXmlDuration(GetStaticMpdDuration(mpd_node)));
|
SecondsToXmlDuration(GetStaticMpdDuration(mpd_node)));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 MpdBuilder::GetStaticMpdDuration(XmlNode* mpd_node) {
|
float MpdBuilder::GetStaticMpdDuration(XmlNode* mpd_node) {
|
||||||
DCHECK(mpd_node);
|
DCHECK(mpd_node);
|
||||||
DCHECK_EQ(MpdBuilder::kStatic, type_);
|
DCHECK_EQ(MpdBuilder::kStatic, type_);
|
||||||
|
|
||||||
|
@ -168,19 +189,17 @@ uint32 MpdBuilder::GetStaticMpdDuration(XmlNode* mpd_node) {
|
||||||
DCHECK_EQ(strcmp(reinterpret_cast<const char*>(period_node->name), "Period"),
|
DCHECK_EQ(strcmp(reinterpret_cast<const char*>(period_node->name), "Period"),
|
||||||
0);
|
0);
|
||||||
|
|
||||||
// TODO(rkuroiwa): Update this so that the duration for each Representation is
|
|
||||||
// (duration / timescale).
|
|
||||||
// Attribute mediaPresentationDuration must be present for 'static' MPD. So
|
// Attribute mediaPresentationDuration must be present for 'static' MPD. So
|
||||||
// setting "P0S" is still required if none of the representaions had a
|
// setting "PT0S" is required even if none of the representaions have duration
|
||||||
// duration attribute.
|
// attribute.
|
||||||
uint32 max_duration = 0;
|
float max_duration = 0.0f;
|
||||||
for (xmlNodePtr adaptation_set = xmlFirstElementChild(period_node);
|
for (xmlNodePtr adaptation_set = xmlFirstElementChild(period_node);
|
||||||
adaptation_set;
|
adaptation_set;
|
||||||
adaptation_set = xmlNextElementSibling(adaptation_set)) {
|
adaptation_set = xmlNextElementSibling(adaptation_set)) {
|
||||||
for (xmlNodePtr representation = xmlFirstElementChild(adaptation_set);
|
for (xmlNodePtr representation = xmlFirstElementChild(adaptation_set);
|
||||||
representation;
|
representation;
|
||||||
representation = xmlNextElementSibling(representation)) {
|
representation = xmlNextElementSibling(representation)) {
|
||||||
uint32 duration = 0;
|
float duration = 0.0f;
|
||||||
if (GetDurationAttribute(representation, &duration)) {
|
if (GetDurationAttribute(representation, &duration)) {
|
||||||
max_duration = max_duration > duration ? max_duration : duration;
|
max_duration = max_duration > duration ? max_duration : duration;
|
||||||
|
|
||||||
|
@ -206,20 +225,12 @@ AdaptationSet::~AdaptationSet() {}
|
||||||
|
|
||||||
Representation* AdaptationSet::AddRepresentation(const MediaInfo& media_info) {
|
Representation* AdaptationSet::AddRepresentation(const MediaInfo& media_info) {
|
||||||
base::AutoLock scoped_lock(lock_);
|
base::AutoLock scoped_lock(lock_);
|
||||||
if (HasVODOnlyFields(media_info) && HasLiveOnlyFields(media_info)) {
|
|
||||||
LOG(ERROR) << "MediaInfo cannot have both VOD and Live fields.";
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!media_info.has_bandwidth()) {
|
|
||||||
LOG(ERROR) << "MediaInfo missing required bandwidth field.";
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
scoped_ptr<Representation> representation(
|
scoped_ptr<Representation> representation(
|
||||||
new Representation(media_info, representation_counter_->GetNext()));
|
new Representation(media_info, representation_counter_->GetNext()));
|
||||||
|
|
||||||
DCHECK(representation);
|
if (!representation->Init())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
representations_.push_back(representation.get());
|
representations_.push_back(representation.get());
|
||||||
return representation.release();
|
return representation.release();
|
||||||
}
|
}
|
||||||
|
@ -260,6 +271,43 @@ Representation::Representation(const MediaInfo& media_info, uint32 id)
|
||||||
|
|
||||||
Representation::~Representation() {}
|
Representation::~Representation() {}
|
||||||
|
|
||||||
|
bool Representation::Init() {
|
||||||
|
if (!HasRequiredMediaInfoFields())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
codecs_ = GetCodecs(media_info_);
|
||||||
|
if (codecs_.empty()) {
|
||||||
|
LOG(ERROR) << "Missing codec info in MediaInfo.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool has_video_info = media_info_.video_info_size() > 0;
|
||||||
|
const bool has_audio_info = media_info_.audio_info_size() > 0;
|
||||||
|
|
||||||
|
if (!has_video_info && !has_audio_info) {
|
||||||
|
// TODO(rkuroiwa): Allow text input.
|
||||||
|
LOG(ERROR) << "Representation needs video or audio.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!media_info_.container_type() == MediaInfo::CONTAINER_UNKNOWN) {
|
||||||
|
// TODO(rkuroiwa): This might not be the right behavior. Maybe somehow
|
||||||
|
// infer from something else like media file name?
|
||||||
|
LOG(ERROR) << "'container_type' in MediaInfo cannot be CONTAINER_UNKNOWN.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check video and then audio. Usually when there is audio + video, we take
|
||||||
|
// video/<type>.
|
||||||
|
if (has_video_info) {
|
||||||
|
mime_type_ = GetVideoMimeType();
|
||||||
|
} else if (has_audio_info) {
|
||||||
|
mime_type_ = GetAudioMimeType();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Representation::AddContentProtectionElement(
|
void Representation::AddContentProtectionElement(
|
||||||
const ContentProtectionElement& content_protection_element) {
|
const ContentProtectionElement& content_protection_element) {
|
||||||
base::AutoLock scoped_lock(lock_);
|
base::AutoLock scoped_lock(lock_);
|
||||||
|
@ -289,32 +337,32 @@ xml::ScopedXmlPtr<xmlNode>::type Representation::GetXml() {
|
||||||
return xml::ScopedXmlPtr<xmlNode>::type();
|
return xml::ScopedXmlPtr<xmlNode>::type();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Two 'Mandatory' fields for Representation.
|
// Mandatory fields for Representation.
|
||||||
representation.SetId(id_);
|
representation.SetId(id_);
|
||||||
representation.SetIntegerAttribute("bandwidth", media_info_.bandwidth());
|
representation.SetIntegerAttribute("bandwidth", media_info_.bandwidth());
|
||||||
|
representation.SetStringAttribute("codecs", codecs_);
|
||||||
|
representation.SetStringAttribute("mimeType", mime_type_);
|
||||||
|
|
||||||
const bool has_video_info = media_info_.video_info_size() > 0;
|
const bool has_video_info = media_info_.video_info_size() > 0;
|
||||||
const bool has_audio_info = media_info_.audio_info_size() > 0;
|
const bool has_audio_info = media_info_.audio_info_size() > 0;
|
||||||
if (has_video_info || has_audio_info) {
|
|
||||||
const std::string codecs = GetCodecs(media_info_);
|
|
||||||
if (!codecs.empty())
|
|
||||||
representation.SetStringAttribute("codecs", codecs);
|
|
||||||
|
|
||||||
if (has_video_info) {
|
if (has_video_info &&
|
||||||
if (!representation.AddVideoInfo(media_info_.video_info()))
|
!representation.AddVideoInfo(media_info_.video_info())) {
|
||||||
|
LOG(ERROR) << "Failed to add video info to Representation XML.";
|
||||||
return xml::ScopedXmlPtr<xmlNode>::type();
|
return xml::ScopedXmlPtr<xmlNode>::type();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_audio_info) {
|
if (has_audio_info &&
|
||||||
if (!representation.AddAudioInfo(media_info_.audio_info()))
|
!representation.AddAudioInfo(media_info_.audio_info())) {
|
||||||
|
LOG(ERROR) << "Failed to add audio info to Representation XML.";
|
||||||
return xml::ScopedXmlPtr<xmlNode>::type();
|
return xml::ScopedXmlPtr<xmlNode>::type();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(rkuroiwa): Add TextInfo.
|
// TODO(rkuroiwa): Add TextInfo.
|
||||||
// TODO(rkuroiwa): Add ContentProtection info.
|
// TODO(rkuroiwa): Add ContentProtection info.
|
||||||
if (HasVODOnlyFields(media_info_)) {
|
if (HasVODOnlyFields(media_info_) &&
|
||||||
if (!representation.AddVODOnlyInfo(media_info_))
|
!representation.AddVODOnlyInfo(media_info_)) {
|
||||||
|
LOG(ERROR) << "Failed to add VOD info.";
|
||||||
return xml::ScopedXmlPtr<xmlNode>::type();
|
return xml::ScopedXmlPtr<xmlNode>::type();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,4 +371,31 @@ xml::ScopedXmlPtr<xmlNode>::type Representation::GetXml() {
|
||||||
return representation.PassScopedPtr();
|
return representation.PassScopedPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Representation::HasRequiredMediaInfoFields() {
|
||||||
|
if (HasVODOnlyFields(media_info_) && HasLiveOnlyFields(media_info_)) {
|
||||||
|
LOG(ERROR) << "MediaInfo cannot have both VOD and Live fields.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!media_info_.has_bandwidth()) {
|
||||||
|
LOG(ERROR) << "MediaInfo missing required field: bandwidth.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!media_info_.has_container_type()) {
|
||||||
|
LOG(ERROR) << "MediaInfo missing required field: container_type.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Representation::GetVideoMimeType() const {
|
||||||
|
return GetMimeType("video", media_info_.container_type());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Representation::GetAudioMimeType() const {
|
||||||
|
return GetMimeType("audio", media_info_.container_type());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dash_packager
|
} // namespace dash_packager
|
||||||
|
|
|
@ -63,7 +63,7 @@ class MpdBuilder {
|
||||||
// Adds 'static' MPD attributes and elements to |mpd_node|. This assumes that
|
// Adds 'static' MPD attributes and elements to |mpd_node|. This assumes that
|
||||||
// the first child element is a Period element.
|
// the first child element is a Period element.
|
||||||
void AddStaticMpdInfo(xml::XmlNode* mpd_node);
|
void AddStaticMpdInfo(xml::XmlNode* mpd_node);
|
||||||
uint32 GetStaticMpdDuration(xml::XmlNode* mpd_node);
|
float GetStaticMpdDuration(xml::XmlNode* mpd_node);
|
||||||
|
|
||||||
MpdType type_;
|
MpdType type_;
|
||||||
std::list<AdaptationSet*> adaptation_sets_;
|
std::list<AdaptationSet*> adaptation_sets_;
|
||||||
|
@ -122,6 +122,8 @@ class Representation {
|
||||||
Representation(const MediaInfo& media_info, uint32 representation_id);
|
Representation(const MediaInfo& media_info, uint32 representation_id);
|
||||||
~Representation();
|
~Representation();
|
||||||
|
|
||||||
|
bool Init();
|
||||||
|
|
||||||
// If |element| has {value, schemeIdUri} set and has
|
// If |element| has {value, schemeIdUri} set and has
|
||||||
// {“value”, “schemeIdUri”} as key for additional_attributes,
|
// {“value”, “schemeIdUri”} as key for additional_attributes,
|
||||||
// then the former is used.
|
// then the former is used.
|
||||||
|
@ -140,6 +142,15 @@ class Representation {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Returns whether |media_info_| has required fields to generate a valid
|
||||||
|
// Representation. Returns true on success, otherwise returns false.
|
||||||
|
bool HasRequiredMediaInfoFields();
|
||||||
|
|
||||||
|
// Note: Because 'mimeType' is a required field for a valid MPD, these return
|
||||||
|
// strings.
|
||||||
|
std::string GetVideoMimeType() const;
|
||||||
|
std::string GetAudioMimeType() const;
|
||||||
|
|
||||||
MediaInfo media_info_;
|
MediaInfo media_info_;
|
||||||
std::list<ContentProtectionElement> content_protection_elements_;
|
std::list<ContentProtectionElement> content_protection_elements_;
|
||||||
std::list<std::pair<uint64, uint64> > segment_starttime_duration_pairs_;
|
std::list<std::pair<uint64, uint64> > segment_starttime_duration_pairs_;
|
||||||
|
@ -147,6 +158,8 @@ class Representation {
|
||||||
base::Lock lock_;
|
base::Lock lock_;
|
||||||
|
|
||||||
const uint32 id_;
|
const uint32 id_;
|
||||||
|
std::string mime_type_;
|
||||||
|
std::string codecs_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(Representation);
|
DISALLOW_COPY_AND_ASSIGN(Representation);
|
||||||
};
|
};
|
||||||
|
|
|
@ -77,11 +77,11 @@ std::string GetCodecs(const MediaInfo& media_info) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SecondsToXmlDuration(uint32 seconds) {
|
std::string SecondsToXmlDuration(float seconds) {
|
||||||
return "PT" + base::UintToString(seconds) + "S";
|
return "PT" + base::DoubleToString(seconds) + "S";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetDurationAttribute(xmlNodePtr node, uint32* duration) {
|
bool GetDurationAttribute(xmlNodePtr node, float* duration) {
|
||||||
DCHECK(node);
|
DCHECK(node);
|
||||||
DCHECK(duration);
|
DCHECK(duration);
|
||||||
static const char kDuration[] = "duration";
|
static const char kDuration[] = "duration";
|
||||||
|
@ -91,8 +91,14 @@ bool GetDurationAttribute(xmlNodePtr node, uint32* duration) {
|
||||||
if (!duration_value)
|
if (!duration_value)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return base::StringToUint(reinterpret_cast<const char*>(duration_value.get()),
|
double duration_double_precision = 0.0;
|
||||||
duration);
|
if (!base::StringToDouble(reinterpret_cast<const char*>(duration_value.get()),
|
||||||
|
&duration_double_precision)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*duration = static_cast<float>(duration_double_precision);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dash_packager
|
} // namespace dash_packager
|
||||||
|
|
|
@ -26,12 +26,10 @@ void RemoveDuplicateAttributes(
|
||||||
// comma.
|
// comma.
|
||||||
std::string GetCodecs(const MediaInfo& media_info);
|
std::string GetCodecs(const MediaInfo& media_info);
|
||||||
|
|
||||||
// TODO(rkuroiwa): This probably needs to change to floating point number.
|
std::string SecondsToXmlDuration(float seconds);
|
||||||
// Returns "P<seconds>S".
|
|
||||||
std::string SecondsToXmlDuration(uint32 seconds);
|
|
||||||
|
|
||||||
// Tries to get "duration" attribute from |node|. On success |duration| is set.
|
// Tries to get "duration" attribute from |node|. On success |duration| is set.
|
||||||
bool GetDurationAttribute(xmlNodePtr node, uint32* duration);
|
bool GetDurationAttribute(xmlNodePtr node, float* duration);
|
||||||
|
|
||||||
} // namespace dash_packager
|
} // namespace dash_packager
|
||||||
|
|
||||||
|
|
|
@ -180,8 +180,6 @@ bool RepresentationXmlNode::AddAudioInfo(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(rkuroiwa): Add 'mimeType' field to this element. Maybe MPD builder
|
|
||||||
// classes should do this or maybe in this class.
|
|
||||||
bool RepresentationXmlNode::AddVODOnlyInfo(const MediaInfo& media_info) {
|
bool RepresentationXmlNode::AddVODOnlyInfo(const MediaInfo& media_info) {
|
||||||
const bool need_segment_base = media_info.has_index_range() ||
|
const bool need_segment_base = media_info.has_index_range() ||
|
||||||
media_info.has_init_range() ||
|
media_info.has_init_range() ||
|
||||||
|
|
Loading…
Reference in New Issue