Check for sample aspect ratio, frame duration, and time scale
- These fields are required to generate DASH IOP compliant MPDs of type=video. Change-Id: I142ecd662e454ae10d06d66aa5519171f5995303
This commit is contained in:
parent
b87d27a23b
commit
fcaac3de33
|
@ -20,13 +20,25 @@ message MediaInfo {
|
||||||
|
|
||||||
message VideoInfo {
|
message VideoInfo {
|
||||||
optional string codec = 1;
|
optional string codec = 1;
|
||||||
|
|
||||||
|
// The width and height of the actual number of pixels. This will not be the
|
||||||
|
// same as the visual width and height if the sample aspect ratio (sar)
|
||||||
|
// is not 1:1.
|
||||||
optional uint32 width = 2;
|
optional uint32 width = 2;
|
||||||
optional uint32 height = 3;
|
optional uint32 height = 3;
|
||||||
|
|
||||||
optional uint32 time_scale = 4;
|
optional uint32 time_scale = 4;
|
||||||
// Relative to |time_scale|. IOW |time_scale| / |frame_duration| is the
|
// Relative to |time_scale|. IOW |time_scale| / |frame_duration| is the
|
||||||
// framerate.
|
// framerate.
|
||||||
optional uint64 frame_duration = 5;
|
optional uint64 frame_duration = 5;
|
||||||
optional bytes decoder_config = 6;
|
optional bytes decoder_config = 6;
|
||||||
|
|
||||||
|
// pixel_width:pixel_height is the the sample aspect ratio (sar) of the
|
||||||
|
// video.
|
||||||
|
// Note that (pixel_width * width):(pixel_height * height) is the picture
|
||||||
|
// aspect ratio, or the @par attribute set on AdaptationSet element.
|
||||||
|
optional uint32 pixel_width = 7;
|
||||||
|
optional uint32 pixel_height = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AudioInfo {
|
message AudioInfo {
|
||||||
|
|
|
@ -35,9 +35,8 @@ using xml::AdaptationSetXmlNode;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::string GetMimeType(
|
std::string GetMimeType(const std::string& prefix,
|
||||||
const std::string& prefix,
|
MediaInfo::ContainerType container_type) {
|
||||||
MediaInfo::ContainerType container_type) {
|
|
||||||
switch (container_type) {
|
switch (container_type) {
|
||||||
case MediaInfo::CONTAINER_MP4:
|
case MediaInfo::CONTAINER_MP4:
|
||||||
return prefix + "/mp4";
|
return prefix + "/mp4";
|
||||||
|
@ -60,7 +59,8 @@ void AddMpdNameSpaceInfo(XmlNode* mpd) {
|
||||||
|
|
||||||
static const char kXmlNamespace[] = "urn:mpeg:DASH:schema:MPD:2011";
|
static const char kXmlNamespace[] = "urn:mpeg:DASH:schema:MPD:2011";
|
||||||
mpd->SetStringAttribute("xmlns", kXmlNamespace);
|
mpd->SetStringAttribute("xmlns", kXmlNamespace);
|
||||||
static const char kXmlNamespaceXsi[] = "http://www.w3.org/2001/XMLSchema-instance";
|
static const char kXmlNamespaceXsi[] =
|
||||||
|
"http://www.w3.org/2001/XMLSchema-instance";
|
||||||
mpd->SetStringAttribute("xmlns:xsi", kXmlNamespaceXsi);
|
mpd->SetStringAttribute("xmlns:xsi", kXmlNamespaceXsi);
|
||||||
static const char kXmlNamespaceXlink[] = "http://www.w3.org/1999/xlink";
|
static const char kXmlNamespaceXlink[] = "http://www.w3.org/1999/xlink";
|
||||||
mpd->SetStringAttribute("xmlns:xlink", kXmlNamespaceXlink);
|
mpd->SetStringAttribute("xmlns:xlink", kXmlNamespaceXlink);
|
||||||
|
@ -82,8 +82,7 @@ bool IsPeriodNode(xmlNodePtr node) {
|
||||||
// As noted here, we must traverse.
|
// As noted here, we must traverse.
|
||||||
// http://www.xmlsoft.org/tutorial/ar01s04.html
|
// http://www.xmlsoft.org/tutorial/ar01s04.html
|
||||||
xmlNodePtr FindPeriodNode(XmlNode* xml_node) {
|
xmlNodePtr FindPeriodNode(XmlNode* xml_node) {
|
||||||
for (xmlNodePtr node = xml_node->GetRawPtr()->xmlChildrenNode;
|
for (xmlNodePtr node = xml_node->GetRawPtr()->xmlChildrenNode; node != NULL;
|
||||||
node != NULL;
|
|
||||||
node = node->next) {
|
node = node->next) {
|
||||||
if (IsPeriodNode(node))
|
if (IsPeriodNode(node))
|
||||||
return node;
|
return node;
|
||||||
|
@ -103,12 +102,9 @@ std::string XmlDateTimeNowWithOffset(int32_t offset_seconds) {
|
||||||
base::Time::Exploded time_exploded;
|
base::Time::Exploded time_exploded;
|
||||||
time.UTCExplode(&time_exploded);
|
time.UTCExplode(&time_exploded);
|
||||||
|
|
||||||
return base::StringPrintf("%4d-%02d-%02dT%02d:%02d:%02d",
|
return base::StringPrintf("%4d-%02d-%02dT%02d:%02d:%02d", time_exploded.year,
|
||||||
time_exploded.year,
|
time_exploded.month, time_exploded.day_of_month,
|
||||||
time_exploded.month,
|
time_exploded.hour, time_exploded.minute,
|
||||||
time_exploded.day_of_month,
|
|
||||||
time_exploded.hour,
|
|
||||||
time_exploded.minute,
|
|
||||||
time_exploded.second);
|
time_exploded.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,10 +181,42 @@ bool WriteXmlCharArrayToOutput(xmlChar* doc,
|
||||||
return output->Flush();
|
return output->Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MakePathRelative(const std::string& path, const std::string& mpd_dir) {
|
std::string MakePathRelative(const std::string& path,
|
||||||
|
const std::string& mpd_dir) {
|
||||||
return (path.find(mpd_dir) == 0) ? path.substr(mpd_dir.size()) : path;
|
return (path.find(mpd_dir) == 0) ? path.substr(mpd_dir.size()) : path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check whether all the video infos have width and height.
|
||||||
|
// DASH IOP defines required fields for video representations, namely
|
||||||
|
// width, height, framerate, and sar.
|
||||||
|
bool HasRequiredVideoFields(
|
||||||
|
::google::protobuf::RepeatedPtrField<MediaInfo_VideoInfo> video_infos) {
|
||||||
|
CHECK_GT(video_infos.size(), 0);
|
||||||
|
for (int i = 0; i < video_infos.size(); ++i) {
|
||||||
|
const MediaInfo::VideoInfo& info = video_infos.Get(i);
|
||||||
|
if (!info.has_height() || !info.has_width()) {
|
||||||
|
LOG(ERROR)
|
||||||
|
<< "Width and height are required fields for generating a valid MPD.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// These fields are not required for a valid MPD, but required for DASH IOP
|
||||||
|
// compliant MPD. MpdBuilder can keep generating MPDs without these fields.
|
||||||
|
LOG_IF(WARNING, !info.has_time_scale())
|
||||||
|
<< "Video info does not contain timescale required for "
|
||||||
|
"calculating framerate. @frameRate is required for DASH IOP.";
|
||||||
|
LOG_IF(WARNING, !info.has_frame_duration())
|
||||||
|
<< "Video info does not contain frame duration required "
|
||||||
|
"for calculating framerate. @frameRate is required for DASH IOP.";
|
||||||
|
LOG_IF(WARNING, !info.has_pixel_width())
|
||||||
|
<< "Video info does not contain pixel_width to calculate the sample "
|
||||||
|
"aspect ratio required for DASH IOP.";
|
||||||
|
LOG_IF(WARNING, !info.has_pixel_height())
|
||||||
|
<< "Video info does not contain pixel_height to calculate the sample "
|
||||||
|
"aspect ratio required for DASH IOP.";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Spooky static initialization/cleanup of libxml.
|
// Spooky static initialization/cleanup of libxml.
|
||||||
class LibXmlInitializer {
|
class LibXmlInitializer {
|
||||||
public:
|
public:
|
||||||
|
@ -220,9 +248,11 @@ class LibXmlInitializer {
|
||||||
MpdBuilder::MpdBuilder(MpdType type, const MpdOptions& mpd_options)
|
MpdBuilder::MpdBuilder(MpdType type, const MpdOptions& mpd_options)
|
||||||
: type_(type),
|
: type_(type),
|
||||||
mpd_options_(mpd_options),
|
mpd_options_(mpd_options),
|
||||||
adaptation_sets_deleter_(&adaptation_sets_) {}
|
adaptation_sets_deleter_(&adaptation_sets_) {
|
||||||
|
}
|
||||||
|
|
||||||
MpdBuilder::~MpdBuilder() {}
|
MpdBuilder::~MpdBuilder() {
|
||||||
|
}
|
||||||
|
|
||||||
void MpdBuilder::AddBaseUrl(const std::string& base_url) {
|
void MpdBuilder::AddBaseUrl(const std::string& base_url) {
|
||||||
base::AutoLock scoped_lock(lock_);
|
base::AutoLock scoped_lock(lock_);
|
||||||
|
@ -231,9 +261,9 @@ void MpdBuilder::AddBaseUrl(const std::string& base_url) {
|
||||||
|
|
||||||
AdaptationSet* MpdBuilder::AddAdaptationSet(const std::string& lang) {
|
AdaptationSet* MpdBuilder::AddAdaptationSet(const std::string& lang) {
|
||||||
base::AutoLock scoped_lock(lock_);
|
base::AutoLock scoped_lock(lock_);
|
||||||
scoped_ptr<AdaptationSet> adaptation_set(new AdaptationSet(
|
scoped_ptr<AdaptationSet> adaptation_set(
|
||||||
adaptation_set_counter_.GetNext(), lang, mpd_options_,
|
new AdaptationSet(adaptation_set_counter_.GetNext(), lang, mpd_options_,
|
||||||
&representation_counter_));
|
&representation_counter_));
|
||||||
|
|
||||||
DCHECK(adaptation_set);
|
DCHECK(adaptation_set);
|
||||||
adaptation_sets_.push_back(adaptation_set.get());
|
adaptation_sets_.push_back(adaptation_set.get());
|
||||||
|
@ -263,8 +293,8 @@ bool MpdBuilder::WriteMpdToOutput(OutputType* output) {
|
||||||
static const int kNiceFormat = 1;
|
static const int kNiceFormat = 1;
|
||||||
int doc_str_size = 0;
|
int doc_str_size = 0;
|
||||||
xmlChar* doc_str = NULL;
|
xmlChar* doc_str = NULL;
|
||||||
xmlDocDumpFormatMemoryEnc(
|
xmlDocDumpFormatMemoryEnc(doc.get(), &doc_str, &doc_str_size, "UTF-8",
|
||||||
doc.get(), &doc_str, &doc_str_size, "UTF-8", kNiceFormat);
|
kNiceFormat);
|
||||||
|
|
||||||
bool result = WriteXmlCharArrayToOutput(doc_str, doc_str_size, output);
|
bool result = WriteXmlCharArrayToOutput(doc_str, doc_str_size, output);
|
||||||
xmlFree(doc_str);
|
xmlFree(doc_str);
|
||||||
|
@ -330,8 +360,7 @@ xmlDocPtr MpdBuilder::GenerateMpd() {
|
||||||
void MpdBuilder::AddCommonMpdInfo(XmlNode* mpd_node) {
|
void MpdBuilder::AddCommonMpdInfo(XmlNode* mpd_node) {
|
||||||
if (Positive(mpd_options_.min_buffer_time)) {
|
if (Positive(mpd_options_.min_buffer_time)) {
|
||||||
mpd_node->SetStringAttribute(
|
mpd_node->SetStringAttribute(
|
||||||
"minBufferTime",
|
"minBufferTime", SecondsToXmlDuration(mpd_options_.min_buffer_time));
|
||||||
SecondsToXmlDuration(mpd_options_.min_buffer_time));
|
|
||||||
} else {
|
} else {
|
||||||
LOG(ERROR) << "minBufferTime value not specified.";
|
LOG(ERROR) << "minBufferTime value not specified.";
|
||||||
// TODO(tinskip): Propagate error.
|
// TODO(tinskip): Propagate error.
|
||||||
|
@ -368,8 +397,8 @@ void MpdBuilder::AddDynamicMpdInfo(XmlNode* mpd_node) {
|
||||||
double earliest_presentation_time;
|
double earliest_presentation_time;
|
||||||
if (GetEarliestTimestamp(&earliest_presentation_time)) {
|
if (GetEarliestTimestamp(&earliest_presentation_time)) {
|
||||||
availability_start_time_ =
|
availability_start_time_ =
|
||||||
XmlDateTimeNowWithOffset(mpd_options_.availability_time_offset
|
XmlDateTimeNowWithOffset(mpd_options_.availability_time_offset -
|
||||||
- std::ceil(earliest_presentation_time));
|
std::ceil(earliest_presentation_time));
|
||||||
} else {
|
} else {
|
||||||
LOG(ERROR) << "Could not determine the earliest segment presentation "
|
LOG(ERROR) << "Could not determine the earliest segment presentation "
|
||||||
"time for availabilityStartTime calculation.";
|
"time for availabilityStartTime calculation.";
|
||||||
|
@ -377,7 +406,8 @@ void MpdBuilder::AddDynamicMpdInfo(XmlNode* mpd_node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!availability_start_time_.empty())
|
if (!availability_start_time_.empty())
|
||||||
mpd_node->SetStringAttribute("availabilityStartTime", availability_start_time_);
|
mpd_node->SetStringAttribute("availabilityStartTime",
|
||||||
|
availability_start_time_);
|
||||||
|
|
||||||
if (Positive(mpd_options_.minimum_update_period)) {
|
if (Positive(mpd_options_.minimum_update_period)) {
|
||||||
mpd_node->SetStringAttribute(
|
mpd_node->SetStringAttribute(
|
||||||
|
@ -385,14 +415,13 @@ void MpdBuilder::AddDynamicMpdInfo(XmlNode* mpd_node) {
|
||||||
SecondsToXmlDuration(mpd_options_.minimum_update_period));
|
SecondsToXmlDuration(mpd_options_.minimum_update_period));
|
||||||
} else {
|
} else {
|
||||||
LOG(WARNING) << "The profile is dynamic but no minimumUpdatePeriod "
|
LOG(WARNING) << "The profile is dynamic but no minimumUpdatePeriod "
|
||||||
"specified.";
|
"specified.";
|
||||||
}
|
}
|
||||||
|
|
||||||
SetIfPositive(
|
SetIfPositive("timeShiftBufferDepth", mpd_options_.time_shift_buffer_depth,
|
||||||
"timeShiftBufferDepth", mpd_options_.time_shift_buffer_depth, mpd_node);
|
|
||||||
SetIfPositive("suggestedPresentationDelay",
|
|
||||||
mpd_options_.suggested_presentation_delay,
|
|
||||||
mpd_node);
|
mpd_node);
|
||||||
|
SetIfPositive("suggestedPresentationDelay",
|
||||||
|
mpd_options_.suggested_presentation_delay, mpd_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
float MpdBuilder::GetStaticMpdDuration(XmlNode* mpd_node) {
|
float MpdBuilder::GetStaticMpdDuration(XmlNode* mpd_node) {
|
||||||
|
@ -408,8 +437,7 @@ float MpdBuilder::GetStaticMpdDuration(XmlNode* mpd_node) {
|
||||||
// attribute.
|
// attribute.
|
||||||
float max_duration = 0.0f;
|
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)) {
|
||||||
|
@ -433,8 +461,7 @@ bool MpdBuilder::GetEarliestTimestamp(double* timestamp_seconds) {
|
||||||
double earliest_timestamp(-1);
|
double earliest_timestamp(-1);
|
||||||
for (std::list<AdaptationSet*>::const_iterator iter =
|
for (std::list<AdaptationSet*>::const_iterator iter =
|
||||||
adaptation_sets_.begin();
|
adaptation_sets_.begin();
|
||||||
iter != adaptation_sets_.end();
|
iter != adaptation_sets_.end(); ++iter) {
|
||||||
++iter) {
|
|
||||||
double timestamp;
|
double timestamp;
|
||||||
if ((*iter)->GetEarliestTimestamp(×tamp) &&
|
if ((*iter)->GetEarliestTimestamp(×tamp) &&
|
||||||
((earliest_timestamp < 0) || (timestamp < earliest_timestamp))) {
|
((earliest_timestamp < 0) || (timestamp < earliest_timestamp))) {
|
||||||
|
@ -452,8 +479,9 @@ void MpdBuilder::MakePathsRelativeToMpd(const std::string& mpd_path,
|
||||||
MediaInfo* media_info) {
|
MediaInfo* media_info) {
|
||||||
DCHECK(media_info);
|
DCHECK(media_info);
|
||||||
const std::string kFileProtocol("file://");
|
const std::string kFileProtocol("file://");
|
||||||
std::string mpd_file_path = (mpd_path.find(kFileProtocol) == 0) ?
|
std::string mpd_file_path = (mpd_path.find(kFileProtocol) == 0)
|
||||||
mpd_path.substr(kFileProtocol.size()) : mpd_path;
|
? mpd_path.substr(kFileProtocol.size())
|
||||||
|
: mpd_path;
|
||||||
|
|
||||||
if (!mpd_file_path.empty()) {
|
if (!mpd_file_path.empty()) {
|
||||||
std::string mpd_dir(
|
std::string mpd_dir(
|
||||||
|
@ -487,7 +515,8 @@ AdaptationSet::AdaptationSet(uint32_t adaptation_set_id,
|
||||||
DCHECK(counter);
|
DCHECK(counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
AdaptationSet::~AdaptationSet() {}
|
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_);
|
||||||
|
@ -515,7 +544,7 @@ xml::ScopedXmlPtr<xmlNode>::type AdaptationSet::GetXml() {
|
||||||
AdaptationSetXmlNode adaptation_set;
|
AdaptationSetXmlNode adaptation_set;
|
||||||
|
|
||||||
if (!adaptation_set.AddContentProtectionElements(
|
if (!adaptation_set.AddContentProtectionElements(
|
||||||
content_protection_elements_)) {
|
content_protection_elements_)) {
|
||||||
return xml::ScopedXmlPtr<xmlNode>::type();
|
return xml::ScopedXmlPtr<xmlNode>::type();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -542,8 +571,7 @@ bool AdaptationSet::GetEarliestTimestamp(double* timestamp_seconds) {
|
||||||
double earliest_timestamp(-1);
|
double earliest_timestamp(-1);
|
||||||
for (std::list<Representation*>::const_iterator iter =
|
for (std::list<Representation*>::const_iterator iter =
|
||||||
representations_.begin();
|
representations_.begin();
|
||||||
iter != representations_.end();
|
iter != representations_.end(); ++iter) {
|
||||||
++iter) {
|
|
||||||
double timestamp;
|
double timestamp;
|
||||||
if ((*iter)->GetEarliestTimestamp(×tamp) &&
|
if ((*iter)->GetEarliestTimestamp(×tamp) &&
|
||||||
((earliest_timestamp < 0) || (timestamp < earliest_timestamp))) {
|
((earliest_timestamp < 0) || (timestamp < earliest_timestamp))) {
|
||||||
|
@ -567,7 +595,8 @@ Representation::Representation(const MediaInfo& media_info,
|
||||||
start_number_(1) {
|
start_number_(1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Representation::~Representation() {}
|
Representation::~Representation() {
|
||||||
|
}
|
||||||
|
|
||||||
bool Representation::Init() {
|
bool Representation::Init() {
|
||||||
codecs_ = GetCodecs(media_info_);
|
codecs_ = GetCodecs(media_info_);
|
||||||
|
@ -592,10 +621,14 @@ bool Representation::Init() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check video and then audio. Usually when there is audio + video, we take
|
// For mimetypes, this checks the video and then audio. Usually when there is
|
||||||
// video/<type>.
|
// audio + video, we take video/<type>.
|
||||||
if (has_video_info) {
|
if (has_video_info) {
|
||||||
mime_type_ = GetVideoMimeType();
|
mime_type_ = GetVideoMimeType();
|
||||||
|
if (!HasRequiredVideoFields(media_info_.video_info())) {
|
||||||
|
LOG(ERROR) << "Missing required fields to create a video Representation.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else if (has_audio_info) {
|
} else if (has_audio_info) {
|
||||||
mime_type_ = GetAudioMimeType();
|
mime_type_ = GetAudioMimeType();
|
||||||
}
|
}
|
||||||
|
@ -676,7 +709,7 @@ xml::ScopedXmlPtr<xmlNode>::type Representation::GetXml() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!representation.AddContentProtectionElements(
|
if (!representation.AddContentProtectionElements(
|
||||||
content_protection_elements_)) {
|
content_protection_elements_)) {
|
||||||
return xml::ScopedXmlPtr<xmlNode>::type();
|
return xml::ScopedXmlPtr<xmlNode>::type();
|
||||||
}
|
}
|
||||||
if (!representation.AddContentProtectionElementsFromMediaInfo(media_info_))
|
if (!representation.AddContentProtectionElementsFromMediaInfo(media_info_))
|
||||||
|
@ -689,8 +722,8 @@ xml::ScopedXmlPtr<xmlNode>::type Representation::GetXml() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasLiveOnlyFields(media_info_) &&
|
if (HasLiveOnlyFields(media_info_) &&
|
||||||
!representation.AddLiveOnlyInfo(
|
!representation.AddLiveOnlyInfo(media_info_, segment_infos_,
|
||||||
media_info_, segment_infos_, start_number_)) {
|
start_number_)) {
|
||||||
LOG(ERROR) << "Failed to add Live info.";
|
LOG(ERROR) << "Failed to add Live info.";
|
||||||
return xml::ScopedXmlPtr<xmlNode>::type();
|
return xml::ScopedXmlPtr<xmlNode>::type();
|
||||||
}
|
}
|
||||||
|
@ -744,9 +777,9 @@ bool Representation::IsContiguous(uint64_t start_time,
|
||||||
previous.start_time + previous.duration * previous.repeat;
|
previous.start_time + previous.duration * previous.repeat;
|
||||||
if (previous_segment_start_time >= start_time) {
|
if (previous_segment_start_time >= start_time) {
|
||||||
LOG(ERROR) << "Segments should not be out of order segment. Adding segment "
|
LOG(ERROR) << "Segments should not be out of order segment. Adding segment "
|
||||||
"with start_time == " << start_time
|
"with start_time == "
|
||||||
<< " but the previous segment starts at " << previous.start_time
|
<< start_time << " but the previous segment starts at "
|
||||||
<< ".";
|
<< previous.start_time << ".";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -841,9 +874,8 @@ bool Representation::GetEarliestTimestamp(double* timestamp_seconds) {
|
||||||
if (segment_infos_.empty())
|
if (segment_infos_.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
*timestamp_seconds =
|
*timestamp_seconds = static_cast<double>(segment_infos_.begin()->start_time) /
|
||||||
static_cast<double>(segment_infos_.begin()->start_time) /
|
GetTimeScale(media_info_);
|
||||||
GetTimeScale(media_info_);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -178,7 +178,7 @@ class AdaptationSet {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class MpdBuilder;
|
friend class MpdBuilder;
|
||||||
FRIEND_TEST_ALL_PREFIXES(StaticMpdBuilderTest, CheckAdaptationSetId);
|
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckAdaptationSetId);
|
||||||
|
|
||||||
/// @param adaptation_set_id is an ID number for this AdaptationSet.
|
/// @param adaptation_set_id is an ID number for this AdaptationSet.
|
||||||
/// @param representation_counter is a Counter for assigning ID numbers to
|
/// @param representation_counter is a Counter for assigning ID numbers to
|
||||||
|
@ -241,7 +241,12 @@ class Representation {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class AdaptationSet;
|
friend class AdaptationSet;
|
||||||
FRIEND_TEST_ALL_PREFIXES(StaticMpdBuilderTest, CheckRepresentationId);
|
// TODO(rkuroiwa): Consider defining a public factory method that constructs
|
||||||
|
// and Init()s, at least for testing.
|
||||||
|
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, ValidMediaInfo);
|
||||||
|
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, InvalidMediaInfo);
|
||||||
|
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckVideoInfoReflectedInXml);
|
||||||
|
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckRepresentationId);
|
||||||
|
|
||||||
/// @param media_info is a MediaInfo containing information on the media.
|
/// @param media_info is a MediaInfo containing information on the media.
|
||||||
/// @a media_info.bandwidth is required for 'static' profile. If @a
|
/// @a media_info.bandwidth is required for 'static' profile. If @a
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <libxml/xmlstring.h>
|
#include <libxml/xmlstring.h>
|
||||||
|
|
||||||
|
#include "base/strings/string_piece.h"
|
||||||
#include "packager/base/file_util.h"
|
#include "packager/base/file_util.h"
|
||||||
#include "packager/base/logging.h"
|
#include "packager/base/logging.h"
|
||||||
#include "packager/base/strings/string_number_conversions.h"
|
#include "packager/base/strings/string_number_conversions.h"
|
||||||
|
@ -24,6 +25,9 @@ namespace edash_packager {
|
||||||
using base::FilePath;
|
using base::FilePath;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
// Any number for RepresentationId. Required to create a Representation but
|
||||||
|
// not checked in test.
|
||||||
|
const uint32_t kAnyRepresentationId = 1;
|
||||||
const char kSElementTemplate[] =
|
const char kSElementTemplate[] =
|
||||||
"<S t=\"%" PRIu64 "\" d=\"%" PRIu64 "\" r=\"%" PRIu64 "\"/>\n";
|
"<S t=\"%" PRIu64 "\" d=\"%" PRIu64 "\" r=\"%" PRIu64 "\"/>\n";
|
||||||
const char kSElementTemplateWithoutR[] =
|
const char kSElementTemplateWithoutR[] =
|
||||||
|
@ -55,6 +59,16 @@ void CheckIdEqual(uint32_t expected_id, T* node) {
|
||||||
xml::ScopedXmlPtr<xmlNode>::type node_xml(node->GetXml());
|
xml::ScopedXmlPtr<xmlNode>::type node_xml(node->GetXml());
|
||||||
ASSERT_NO_FATAL_FAILURE(ExpectXmlElementIdEqual(node_xml.get(), expected_id));
|
ASSERT_NO_FATAL_FAILURE(ExpectXmlElementIdEqual(node_xml.get(), expected_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExpectAttributeHasString(base::StringPiece attribute,
|
||||||
|
base::StringPiece expected_value,
|
||||||
|
xmlNodePtr node) {
|
||||||
|
xml::ScopedXmlPtr<xmlChar>::type attribute_xml_str(
|
||||||
|
xmlGetProp(node, BAD_CAST attribute.data()));
|
||||||
|
ASSERT_TRUE(attribute_xml_str);
|
||||||
|
EXPECT_STREQ(expected_value.data(),
|
||||||
|
reinterpret_cast<const char*>(attribute_xml_str.get()));
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
template <MpdBuilder::MpdType type>
|
template <MpdBuilder::MpdType type>
|
||||||
|
@ -95,6 +109,10 @@ class MpdBuilderTest: public ::testing::Test {
|
||||||
|
|
||||||
class StaticMpdBuilderTest : public MpdBuilderTest<MpdBuilder::kStatic> {};
|
class StaticMpdBuilderTest : public MpdBuilderTest<MpdBuilder::kStatic> {};
|
||||||
|
|
||||||
|
// Use this test name for things that are common to both static an dynamic
|
||||||
|
// mpd builder tests.
|
||||||
|
typedef StaticMpdBuilderTest CommonMpdBuilderTest;
|
||||||
|
|
||||||
class DynamicMpdBuilderTest : public MpdBuilderTest<MpdBuilder::kDynamic> {
|
class DynamicMpdBuilderTest : public MpdBuilderTest<MpdBuilder::kDynamic> {
|
||||||
public:
|
public:
|
||||||
virtual ~DynamicMpdBuilderTest() {}
|
virtual ~DynamicMpdBuilderTest() {}
|
||||||
|
@ -114,6 +132,9 @@ class DynamicMpdBuilderTest : public MpdBuilderTest<MpdBuilder::kDynamic> {
|
||||||
" width: 720\n"
|
" width: 720\n"
|
||||||
" height: 480\n"
|
" height: 480\n"
|
||||||
" time_scale: 10\n"
|
" time_scale: 10\n"
|
||||||
|
" frame_duration: 5\n"
|
||||||
|
" pixel_width: 1\n"
|
||||||
|
" pixel_height: 1\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"reference_time_scale: %u\n"
|
"reference_time_scale: %u\n"
|
||||||
"container_type: 1\n"
|
"container_type: 1\n"
|
||||||
|
@ -182,7 +203,7 @@ class SegmentTemplateTest : public DynamicMpdBuilderTest {
|
||||||
" <AdaptationSet id=\"0\">\n"
|
" <AdaptationSet id=\"0\">\n"
|
||||||
" <Representation id=\"0\" bandwidth=\"%" PRIu64 "\" "
|
" <Representation id=\"0\" bandwidth=\"%" PRIu64 "\" "
|
||||||
"codecs=\"avc1.010101\" mimeType=\"video/mp4\" width=\"720\" "
|
"codecs=\"avc1.010101\" mimeType=\"video/mp4\" width=\"720\" "
|
||||||
"height=\"480\">\n"
|
"height=\"480\" frameRate=\"10/5\" sar=\"1:1\">\n"
|
||||||
" <SegmentTemplate timescale=\"1000\" "
|
" <SegmentTemplate timescale=\"1000\" "
|
||||||
"initialization=\"init.mp4\" media=\"$Time$.mp4\">\n"
|
"initialization=\"init.mp4\" media=\"$Time$.mp4\">\n"
|
||||||
" <SegmentTimeline>\n%s"
|
" <SegmentTimeline>\n%s"
|
||||||
|
@ -233,6 +254,9 @@ class TimeShiftBufferDepthTest : public SegmentTemplateTest {
|
||||||
" width: 720\n"
|
" width: 720\n"
|
||||||
" height: 480\n"
|
" height: 480\n"
|
||||||
" time_scale: 10\n"
|
" time_scale: 10\n"
|
||||||
|
" frame_duration: 2\n"
|
||||||
|
" pixel_width: 1\n"
|
||||||
|
" pixel_height: 1\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"reference_time_scale: %u\n"
|
"reference_time_scale: %u\n"
|
||||||
"container_type: 1\n"
|
"container_type: 1\n"
|
||||||
|
@ -261,7 +285,7 @@ class TimeShiftBufferDepthTest : public SegmentTemplateTest {
|
||||||
" <AdaptationSet id=\"0\">\n"
|
" <AdaptationSet id=\"0\">\n"
|
||||||
" <Representation id=\"0\" bandwidth=\"%" PRIu64 "\" "
|
" <Representation id=\"0\" bandwidth=\"%" PRIu64 "\" "
|
||||||
"codecs=\"avc1.010101\" mimeType=\"video/mp4\" width=\"720\" "
|
"codecs=\"avc1.010101\" mimeType=\"video/mp4\" width=\"720\" "
|
||||||
"height=\"480\">\n"
|
"height=\"480\" frameRate=\"10/2\" sar=\"1:1\">\n"
|
||||||
" <SegmentTemplate timescale=\"1000\" "
|
" <SegmentTemplate timescale=\"1000\" "
|
||||||
"initialization=\"init.mp4\" media=\"$Number$.mp4\" "
|
"initialization=\"init.mp4\" media=\"$Number$.mp4\" "
|
||||||
"startNumber=\"%d\">\n"
|
"startNumber=\"%d\">\n"
|
||||||
|
@ -289,7 +313,74 @@ class TimeShiftBufferDepthTest : public SegmentTemplateTest {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(StaticMpdBuilderTest, CheckAdaptationSetId) {
|
// Verify that Representation::Init() works with all "required" fields of
|
||||||
|
// MedieInfo proto.
|
||||||
|
TEST_F(CommonMpdBuilderTest, ValidMediaInfo) {
|
||||||
|
const char kTestMediaInfo[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: \"avc1\"\n"
|
||||||
|
" width: 720\n"
|
||||||
|
" height: 480\n"
|
||||||
|
" time_scale: 10\n"
|
||||||
|
" frame_duration: 10\n"
|
||||||
|
" pixel_width: 1\n"
|
||||||
|
" pixel_height: 1\n"
|
||||||
|
"}\n"
|
||||||
|
"container_type: 1\n";
|
||||||
|
Representation representation(
|
||||||
|
ConvertToMediaInfo(kTestMediaInfo), MpdOptions(), kAnyRepresentationId);
|
||||||
|
EXPECT_TRUE(representation.Init());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that Representation::Init() fails if a required field is missing.
|
||||||
|
TEST_F(CommonMpdBuilderTest, InvalidMediaInfo) {
|
||||||
|
// Missing width.
|
||||||
|
const char kTestMediaInfo[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: \"avc1\"\n"
|
||||||
|
" height: 480\n"
|
||||||
|
" time_scale: 10\n"
|
||||||
|
" frame_duration: 10\n"
|
||||||
|
" pixel_width: 1\n"
|
||||||
|
" pixel_height: 1\n"
|
||||||
|
"}\n"
|
||||||
|
"container_type: 1\n";
|
||||||
|
Representation representation(
|
||||||
|
ConvertToMediaInfo(kTestMediaInfo), MpdOptions(), kAnyRepresentationId);
|
||||||
|
EXPECT_FALSE(representation.Init());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic check that the fields in video info are in the XML.
|
||||||
|
TEST_F(CommonMpdBuilderTest, CheckVideoInfoReflectedInXml) {
|
||||||
|
const char kTestMediaInfo[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: \"avc1\"\n"
|
||||||
|
" width: 1280\n"
|
||||||
|
" height: 720\n"
|
||||||
|
" time_scale: 10\n"
|
||||||
|
" frame_duration: 10\n"
|
||||||
|
" pixel_width: 1\n"
|
||||||
|
" pixel_height: 1\n"
|
||||||
|
"}\n"
|
||||||
|
"container_type: 1\n";
|
||||||
|
Representation representation(
|
||||||
|
ConvertToMediaInfo(kTestMediaInfo), MpdOptions(), kAnyRepresentationId);
|
||||||
|
EXPECT_TRUE(representation.Init());
|
||||||
|
|
||||||
|
xml::ScopedXmlPtr<xmlNode>::type node_xml(representation.GetXml());
|
||||||
|
EXPECT_NO_FATAL_FAILURE(
|
||||||
|
ExpectAttributeHasString("codecs", "avc1", node_xml.get()));
|
||||||
|
EXPECT_NO_FATAL_FAILURE(
|
||||||
|
ExpectAttributeHasString("width", "1280", node_xml.get()));
|
||||||
|
EXPECT_NO_FATAL_FAILURE(
|
||||||
|
ExpectAttributeHasString("height", "720", node_xml.get()));
|
||||||
|
EXPECT_NO_FATAL_FAILURE(
|
||||||
|
ExpectAttributeHasString("sar", "1:1", node_xml.get()));
|
||||||
|
EXPECT_NO_FATAL_FAILURE(
|
||||||
|
ExpectAttributeHasString("frameRate", "10/10", node_xml.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CommonMpdBuilderTest, CheckAdaptationSetId) {
|
||||||
base::AtomicSequenceNumber sequence_counter;
|
base::AtomicSequenceNumber sequence_counter;
|
||||||
const uint32_t kAdaptationSetId = 42;
|
const uint32_t kAdaptationSetId = 42;
|
||||||
|
|
||||||
|
@ -298,7 +389,7 @@ TEST_F(StaticMpdBuilderTest, CheckAdaptationSetId) {
|
||||||
ASSERT_NO_FATAL_FAILURE(CheckIdEqual(kAdaptationSetId, &adaptation_set));
|
ASSERT_NO_FATAL_FAILURE(CheckIdEqual(kAdaptationSetId, &adaptation_set));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(StaticMpdBuilderTest, CheckRepresentationId) {
|
TEST_F(CommonMpdBuilderTest, CheckRepresentationId) {
|
||||||
const MediaInfo video_media_info = GetTestMediaInfo(kFileNameVideoMediaInfo1);
|
const MediaInfo video_media_info = GetTestMediaInfo(kFileNameVideoMediaInfo1);
|
||||||
const uint32_t kRepresentationId = 1;
|
const uint32_t kRepresentationId = 1;
|
||||||
|
|
||||||
|
|
|
@ -330,7 +330,7 @@ bool RepresentationXmlNode::AddVideoInfo(
|
||||||
// Make sure that all the widths and heights match.
|
// Make sure that all the widths and heights match.
|
||||||
for (int i = 0; i < repeated_video_info.size(); ++i) {
|
for (int i = 0; i < repeated_video_info.size(); ++i) {
|
||||||
const MediaInfo_VideoInfo& video_info = repeated_video_info.Get(i);
|
const MediaInfo_VideoInfo& video_info = repeated_video_info.Get(i);
|
||||||
if (video_info.width() <= 0 || video_info.height() <= 0)
|
if (video_info.width() == 0 || video_info.height() == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (width == 0) {
|
if (width == 0) {
|
||||||
|
@ -352,6 +352,15 @@ bool RepresentationXmlNode::AddVideoInfo(
|
||||||
if (height != 0)
|
if (height != 0)
|
||||||
SetIntegerAttribute("height", height);
|
SetIntegerAttribute("height", height);
|
||||||
|
|
||||||
|
// TODO(rkuroiwa): Because we are going ot make video_info optional instead
|
||||||
|
// of repeated, just using the first video_info.
|
||||||
|
const MediaInfo_VideoInfo& video_info = repeated_video_info.Get(0);
|
||||||
|
SetStringAttribute("frameRate",
|
||||||
|
base::IntToString(video_info.time_scale()) + "/" +
|
||||||
|
base::IntToString(video_info.frame_duration()));
|
||||||
|
|
||||||
|
SetStringAttribute("sar", base::IntToString(video_info.pixel_width()) + ":" +
|
||||||
|
base::IntToString(video_info.pixel_height()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -147,8 +147,8 @@ class RepresentationXmlNode : public RepresentationBaseXmlNode {
|
||||||
/// applicable), false otherwise.
|
/// applicable), false otherwise.
|
||||||
bool AddAudioInfo(const RepeatedAudioInfo& repeated_audio_info);
|
bool AddAudioInfo(const RepeatedAudioInfo& repeated_audio_info);
|
||||||
|
|
||||||
/// Adds fields that are specific to VOD. This ignores @a media_info fields for
|
/// Adds fields that are specific to VOD. This ignores @a media_info fields
|
||||||
/// Live.
|
/// for Live.
|
||||||
/// @param media_info is a MediaInfo with VOD information.
|
/// @param media_info is a MediaInfo with VOD information.
|
||||||
/// @return true on success, false otherwise.
|
/// @return true on success, false otherwise.
|
||||||
bool AddVODOnlyInfo(const MediaInfo& media_info);
|
bool AddVODOnlyInfo(const MediaInfo& media_info);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT10.5S">
|
<MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT10.5S">
|
||||||
<Period>
|
<Period>
|
||||||
<AdaptationSet id="0">
|
<AdaptationSet id="0">
|
||||||
<Representation id="1" bandwidth="7620" codecs="avc1.010101" mimeType="video/mp4" width="720" height="480">
|
<Representation id="1" bandwidth="7620" codecs="avc1.010101" mimeType="video/mp4" width="720" height="480" frameRate="10/1" sar="1:1">
|
||||||
<BaseURL>test_output_file_name1.mp4</BaseURL>
|
<BaseURL>test_output_file_name1.mp4</BaseURL>
|
||||||
<SegmentBase indexRange="121-221" timescale="1000">
|
<SegmentBase indexRange="121-221" timescale="1000">
|
||||||
<Initialization range="0-120"/>
|
<Initialization range="0-120"/>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" availabilityStartTime="2011-12-25T12:30:00" minBufferTime="PT2S" type="dynamic" profiles="urn:mpeg:dash:profile:isoff-live:2011">
|
<MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" availabilityStartTime="2011-12-25T12:30:00" minBufferTime="PT2S" type="dynamic" profiles="urn:mpeg:dash:profile:isoff-live:2011">
|
||||||
<Period start="PT0S">
|
<Period start="PT0S">
|
||||||
<AdaptationSet id="0">
|
<AdaptationSet id="0">
|
||||||
<Representation id="0" bandwidth="102400" codecs="avc1.010101" mimeType="video/mp4" width="720" height="480">
|
<Representation id="0" bandwidth="102400" codecs="avc1.010101" mimeType="video/mp4" width="720" height="480" frameRate="10/5" sar="1:1">
|
||||||
<SegmentTemplate timescale="1000" initialization="init.mp4" media="$Time$.mp4">
|
<SegmentTemplate timescale="1000" initialization="init.mp4" media="$Time$.mp4">
|
||||||
<SegmentTimeline>
|
<SegmentTimeline>
|
||||||
<S t="0" d="10"/>
|
<S t="0" d="10"/>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT10.5S">
|
<MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT10.5S">
|
||||||
<Period>
|
<Period>
|
||||||
<AdaptationSet id="0">
|
<AdaptationSet id="0">
|
||||||
<Representation id="3" bandwidth="7620" codecs="avc1.010101" mimeType="video/mp4" width="720" height="480">
|
<Representation id="3" bandwidth="7620" codecs="avc1.010101" mimeType="video/mp4" width="720" height="480" frameRate="10/1" sar="1:1">
|
||||||
<BaseURL>test_output_file_name1.mp4</BaseURL>
|
<BaseURL>test_output_file_name1.mp4</BaseURL>
|
||||||
<SegmentBase indexRange="121-221" timescale="1000">
|
<SegmentBase indexRange="121-221" timescale="1000">
|
||||||
<Initialization range="0-120"/>
|
<Initialization range="0-120"/>
|
||||||
|
|
|
@ -4,6 +4,9 @@ video_info {
|
||||||
width: 720
|
width: 720
|
||||||
height: 480
|
height: 480
|
||||||
time_scale: 10
|
time_scale: 10
|
||||||
|
frame_duration: 1
|
||||||
|
pixel_width: 1
|
||||||
|
pixel_height: 1
|
||||||
}
|
}
|
||||||
init_range {
|
init_range {
|
||||||
begin: 0
|
begin: 0
|
||||||
|
|
|
@ -4,6 +4,9 @@ video_info {
|
||||||
width: 720
|
width: 720
|
||||||
height: 480
|
height: 480
|
||||||
time_scale: 10
|
time_scale: 10
|
||||||
|
frame_duration: 1
|
||||||
|
pixel_width: 1
|
||||||
|
pixel_height: 1
|
||||||
}
|
}
|
||||||
init_range {
|
init_range {
|
||||||
begin: 0
|
begin: 0
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT10.5S">
|
<MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT10.5S">
|
||||||
<Period>
|
<Period>
|
||||||
<AdaptationSet id="0">
|
<AdaptationSet id="0">
|
||||||
<Representation id="0" bandwidth="7620" codecs="avc1.010101" mimeType="video/mp4" width="720" height="480">
|
<Representation id="0" bandwidth="7620" codecs="avc1.010101" mimeType="video/mp4" width="720" height="480" frameRate="10/1" sar="1:1">
|
||||||
<BaseURL>test_output_file_name1.mp4</BaseURL>
|
<BaseURL>test_output_file_name1.mp4</BaseURL>
|
||||||
<SegmentBase indexRange="121-221" timescale="1000">
|
<SegmentBase indexRange="121-221" timescale="1000">
|
||||||
<Initialization range="0-120"/>
|
<Initialization range="0-120"/>
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
<MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT10.5S">
|
<MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT10.5S">
|
||||||
<Period>
|
<Period>
|
||||||
<AdaptationSet id="0">
|
<AdaptationSet id="0">
|
||||||
<Representation id="0" bandwidth="7620" codecs="avc1.010101" mimeType="video/mp4" width="720" height="480">
|
<Representation id="0" bandwidth="7620" codecs="avc1.010101" mimeType="video/mp4" width="720" height="480" frameRate="10/1" sar="1:1">
|
||||||
<BaseURL>test_output_file_name1.mp4</BaseURL>
|
<BaseURL>test_output_file_name1.mp4</BaseURL>
|
||||||
<SegmentBase indexRange="121-221" timescale="1000">
|
<SegmentBase indexRange="121-221" timescale="1000">
|
||||||
<Initialization range="0-120"/>
|
<Initialization range="0-120"/>
|
||||||
</SegmentBase>
|
</SegmentBase>
|
||||||
</Representation>
|
</Representation>
|
||||||
<Representation id="1" bandwidth="5000" codecs="avc1.010101" mimeType="video/mp4" width="480" height="360">
|
<Representation id="1" bandwidth="5000" codecs="avc1.010101" mimeType="video/mp4" width="480" height="360" frameRate="20/20" sar="2:1">
|
||||||
<BaseURL>test_output_file_name2.mp4</BaseURL>
|
<BaseURL>test_output_file_name2.mp4</BaseURL>
|
||||||
<SegmentBase indexRange="54-100" timescale="50">
|
<SegmentBase indexRange="54-100" timescale="50">
|
||||||
<Initialization range="0-53"/>
|
<Initialization range="0-53"/>
|
||||||
|
|
|
@ -4,6 +4,9 @@ video_info {
|
||||||
width: 480
|
width: 480
|
||||||
height: 360
|
height: 360
|
||||||
time_scale: 20
|
time_scale: 20
|
||||||
|
frame_duration: 20
|
||||||
|
pixel_width: 2
|
||||||
|
pixel_height: 1
|
||||||
}
|
}
|
||||||
init_range {
|
init_range {
|
||||||
begin: 0
|
begin: 0
|
||||||
|
|
|
@ -16,6 +16,9 @@ namespace edash_packager {
|
||||||
class MediaInfo;
|
class MediaInfo;
|
||||||
|
|
||||||
// File names that could be used to call GetTestDataFilePath().
|
// File names that could be used to call GetTestDataFilePath().
|
||||||
|
// TODO(rkuroiwa): Seems like too may indirection. Maybe put the definition
|
||||||
|
// of the proto instance in this file. Or just remove this and put it in the
|
||||||
|
// test.
|
||||||
const char kFileNameVideoMediaInfo1[] = "video_media_info1.txt";
|
const char kFileNameVideoMediaInfo1[] = "video_media_info1.txt";
|
||||||
const char kFileNameVideoMediaInfo2[] = "video_media_info2.txt";
|
const char kFileNameVideoMediaInfo2[] = "video_media_info2.txt";
|
||||||
const char kFileNameAudioMediaInfo1[] = "audio_media_info1.txt";
|
const char kFileNameAudioMediaInfo1[] = "audio_media_info1.txt";
|
||||||
|
|
Loading…
Reference in New Issue