From 2cf673055cc5591f32b88327b6e3ae89b82e268b Mon Sep 17 00:00:00 2001 From: Rintaro Kuroiwa Date: Tue, 9 Jun 2015 16:58:32 -0700 Subject: [PATCH] Make {Video,Audio,Text}Info optional - The fields used to be repeated but there is no use case at the moment. - DASH IOP explicitly disallows segments with multiple tracks. Change-Id: Ia0af2048210c546dfaa178735605a81052ea1123 --- .../media/event/mpd_notify_muxer_listener.cc | 4 +- .../media/event/mpd_notify_muxer_listener.h | 2 +- packager/media/event/muxer_listener.h | 6 +- .../media/event/muxer_listener_internal.cc | 33 ++---- .../media/event/muxer_listener_internal.h | 2 +- .../vod_media_info_dump_muxer_listener.cc | 8 +- .../vod_media_info_dump_muxer_listener.h | 2 +- ...media_info_dump_muxer_listener_unittest.cc | 12 +- packager/media/formats/mp4/mp4_muxer.cc | 19 ++- packager/media/formats/mp4/mp4_muxer.h | 3 - packager/mpd/base/media_info.proto | 9 +- packager/mpd/base/mpd_builder.cc | 71 ++++++----- packager/mpd/base/mpd_builder_unittest.cc | 2 +- packager/mpd/base/mpd_utils.cc | 45 ++----- packager/mpd/base/simple_mpd_notifier.cc | 10 +- packager/mpd/base/xml/xml_node.cc | 112 ++++-------------- packager/mpd/base/xml/xml_node.h | 27 ++--- packager/mpd/util/mpd_writer.cc | 30 ++--- 18 files changed, 134 insertions(+), 263 deletions(-) diff --git a/packager/media/event/mpd_notify_muxer_listener.cc b/packager/media/event/mpd_notify_muxer_listener.cc index 883b68ab79..aa2ca47456 100644 --- a/packager/media/event/mpd_notify_muxer_listener.cc +++ b/packager/media/event/mpd_notify_muxer_listener.cc @@ -34,13 +34,13 @@ void MpdNotifyMuxerListener::SetContentProtectionSchemeIdUri( void MpdNotifyMuxerListener::OnMediaStart( const MuxerOptions& muxer_options, - const std::vector& stream_infos, + const StreamInfo& stream_info, uint32_t time_scale, ContainerType container_type, bool is_encrypted) { scoped_ptr media_info(new MediaInfo()); if (!internal::GenerateMediaInfo(muxer_options, - stream_infos, + stream_info, time_scale, container_type, media_info.get())) { diff --git a/packager/media/event/mpd_notify_muxer_listener.h b/packager/media/event/mpd_notify_muxer_listener.h index 18a29ff9d2..1d8cb119b0 100644 --- a/packager/media/event/mpd_notify_muxer_listener.h +++ b/packager/media/event/mpd_notify_muxer_listener.h @@ -37,7 +37,7 @@ class MpdNotifyMuxerListener : public MuxerListener { /// @name MuxerListener implementation overrides. /// @{ virtual void OnMediaStart(const MuxerOptions& muxer_options, - const std::vector& stream_infos, + const StreamInfo& stream_info, uint32_t time_scale, ContainerType container_type, bool is_encrypted) OVERRIDE; diff --git a/packager/media/event/muxer_listener.h b/packager/media/event/muxer_listener.h index 363e013c50..e3e338a9d0 100644 --- a/packager/media/event/muxer_listener.h +++ b/packager/media/event/muxer_listener.h @@ -37,10 +37,10 @@ class MuxerListener { // Called when muxing starts. This event happens before any other events. // For MPEG DASH Live profile, the initialization segment information is // available from StreamInfo. - // |time_scale| is a reference time scale regardless of the time scale(s) - // specified in |stream_infos|. + // |time_scale| is a reference time scale that overrides the time scale + // specified in |stream_info|. virtual void OnMediaStart(const MuxerOptions& muxer_options, - const std::vector& stream_infos, + const StreamInfo& stream_info, uint32_t time_scale, ContainerType container_type, bool is_encrypted) = 0; diff --git a/packager/media/event/muxer_listener_internal.cc b/packager/media/event/muxer_listener_internal.cc index 6c5b77dc06..e12a5dd194 100644 --- a/packager/media/event/muxer_listener_internal.cc +++ b/packager/media/event/muxer_listener_internal.cc @@ -80,7 +80,7 @@ void AddVideoInfo(const VideoStreamInfo* video_stream_info, MediaInfo* media_info) { DCHECK(video_stream_info); DCHECK(media_info); - MediaInfo_VideoInfo* video_info = media_info->add_video_info(); + MediaInfo_VideoInfo* video_info = media_info->mutable_video_info(); video_info->set_codec(video_stream_info->codec_string()); video_info->set_width(video_stream_info->width()); video_info->set_height(video_stream_info->height()); @@ -96,7 +96,7 @@ void AddAudioInfo(const AudioStreamInfo* audio_stream_info, MediaInfo* media_info) { DCHECK(audio_stream_info); DCHECK(media_info); - MediaInfo_AudioInfo* audio_info = media_info->add_audio_info(); + MediaInfo_AudioInfo* audio_info = media_info->mutable_audio_info(); audio_info->set_codec(audio_stream_info->codec_string()); audio_info->set_sampling_frequency(audio_stream_info->sampling_frequency()); audio_info->set_time_scale(audio_stream_info->time_scale()); @@ -114,24 +114,15 @@ void AddAudioInfo(const AudioStreamInfo* audio_stream_info, } } -void SetMediaInfoStreamInfo(const std::vector& stream_infos, +void SetMediaInfoStreamInfo(const StreamInfo& stream_info, MediaInfo* media_info) { - typedef std::vector::const_iterator StreamInfoIterator; - for (StreamInfoIterator it = stream_infos.begin(); - it != stream_infos.end(); - ++it) { - const StreamInfo* stream_info = *it; - if (!stream_info) - continue; - - if (stream_info->stream_type() == kStreamAudio) { - AddAudioInfo(static_cast(stream_info), - media_info); - } else { - DCHECK_EQ(stream_info->stream_type(), kStreamVideo); - AddVideoInfo(static_cast(stream_info), - media_info); - } + if (stream_info.stream_type() == kStreamAudio) { + AddAudioInfo(static_cast(&stream_info), + media_info); + } else { + DCHECK_EQ(stream_info.stream_type(), kStreamVideo); + AddVideoInfo(static_cast(&stream_info), + media_info); } } @@ -150,14 +141,14 @@ void SetMediaInfoMuxerOptions(const MuxerOptions& muxer_options, } // namespace bool GenerateMediaInfo(const MuxerOptions& muxer_options, - const std::vector& stream_infos, + const StreamInfo& stream_info, uint32_t reference_time_scale, MuxerListener::ContainerType container_type, MediaInfo* media_info) { DCHECK(media_info); SetMediaInfoMuxerOptions(muxer_options, media_info); - SetMediaInfoStreamInfo(stream_infos, media_info); + SetMediaInfoStreamInfo(stream_info, media_info); media_info->set_reference_time_scale(reference_time_scale); SetMediaInfoContainerType(container_type, media_info); if (muxer_options.bandwidth > 0) diff --git a/packager/media/event/muxer_listener_internal.h b/packager/media/event/muxer_listener_internal.h index 8348057536..a3e28d82ef 100644 --- a/packager/media/event/muxer_listener_internal.h +++ b/packager/media/event/muxer_listener_internal.h @@ -28,7 +28,7 @@ namespace internal { /// @param[out] media_info points to the MediaInfo object to be filled. /// @return true on success, false otherwise. bool GenerateMediaInfo(const MuxerOptions& muxer_options, - const std::vector& stream_infos, + const StreamInfo& stream_info, uint32_t reference_time_scale_, MuxerListener::ContainerType container_type, MediaInfo* media_info); diff --git a/packager/media/event/vod_media_info_dump_muxer_listener.cc b/packager/media/event/vod_media_info_dump_muxer_listener.cc index 4e15c49172..a52624f82e 100644 --- a/packager/media/event/vod_media_info_dump_muxer_listener.cc +++ b/packager/media/event/vod_media_info_dump_muxer_listener.cc @@ -31,14 +31,14 @@ void VodMediaInfoDumpMuxerListener::SetContentProtectionSchemeIdUri( void VodMediaInfoDumpMuxerListener::OnMediaStart( const MuxerOptions& muxer_options, - const std::vector& stream_infos, + const StreamInfo& stream_info, uint32_t time_scale, ContainerType container_type, bool is_encrypted) { DCHECK(muxer_options.single_segment); media_info_.reset(new MediaInfo()); if (!internal::GenerateMediaInfo(muxer_options, - stream_infos, + stream_info, time_scale, container_type, media_info_.get())) { @@ -58,8 +58,8 @@ void VodMediaInfoDumpMuxerListener::OnMediaStart( void VodMediaInfoDumpMuxerListener::OnSampleDurationReady( uint32_t sample_duration) { // Assume one VideoInfo. - if (media_info_->video_info_size() > 0) { - media_info_->mutable_video_info(0)->set_frame_duration(sample_duration); + if (media_info_->has_video_info()) { + media_info_->mutable_video_info()->set_frame_duration(sample_duration); } } diff --git a/packager/media/event/vod_media_info_dump_muxer_listener.h b/packager/media/event/vod_media_info_dump_muxer_listener.h index 7ed271cb9a..144957366d 100644 --- a/packager/media/event/vod_media_info_dump_muxer_listener.h +++ b/packager/media/event/vod_media_info_dump_muxer_listener.h @@ -37,7 +37,7 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener { /// @name MuxerListener implementation overrides. /// @{ virtual void OnMediaStart(const MuxerOptions& muxer_options, - const std::vector& stream_infos, + const StreamInfo& stream_info, uint32_t time_scale, ContainerType container_type, bool is_encrypted) OVERRIDE; diff --git a/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc b/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc index 7deece19f2..610e9faf92 100644 --- a/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc +++ b/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc @@ -167,13 +167,13 @@ class VodMediaInfoDumpMuxerListenerTest : public ::testing::Test { } void FireOnMediaStartWithDefaultMuxerOptions( - const std::vector stream_infos, + const StreamInfo& stream_info, bool enable_encryption) { MuxerOptions muxer_options; SetDefaultMuxerOptionsValues(&muxer_options); const uint32_t kReferenceTimeScale = 1000; listener_->OnMediaStart(muxer_options, - stream_infos, + stream_info, kReferenceTimeScale, MuxerListener::kContainerMp4, enable_encryption); @@ -212,10 +212,8 @@ class VodMediaInfoDumpMuxerListenerTest : public ::testing::Test { TEST_F(VodMediaInfoDumpMuxerListenerTest, UnencryptedStream_Normal) { scoped_refptr stream_info = CreateVideoStreamInfo(GetDefaultVideoStreamInfoParams()); - std::vector stream_infos; - stream_infos.push_back(stream_info.get()); - FireOnMediaStartWithDefaultMuxerOptions(stream_infos, !kEnableEncryption); + FireOnMediaStartWithDefaultMuxerOptions(*stream_info, !kEnableEncryption); OnMediaEndParameters media_end_param = GetDefaultOnMediaEndParams(); FireOnMediaEndWithParams(media_end_param); @@ -247,10 +245,8 @@ TEST_F(VodMediaInfoDumpMuxerListenerTest, EncryptedStream_Normal) { scoped_refptr stream_info = CreateVideoStreamInfo(GetDefaultVideoStreamInfoParams()); - std::vector stream_infos; - stream_infos.push_back(stream_info.get()); - FireOnMediaStartWithDefaultMuxerOptions(stream_infos, kEnableEncryption); + FireOnMediaStartWithDefaultMuxerOptions(*stream_info, kEnableEncryption); OnMediaEndParameters media_end_param = GetDefaultOnMediaEndParams(); FireOnMediaEndWithParams(media_end_param); diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc index 5fdc30bb31..bc3ec080c6 100644 --- a/packager/media/formats/mp4/mp4_muxer.cc +++ b/packager/media/formats/mp4/mp4_muxer.cc @@ -204,15 +204,6 @@ void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info, sample_description.audio_entries.push_back(audio); } -void MP4Muxer::GetStreamInfo(std::vector* stream_infos) { - DCHECK(stream_infos); - const std::vector& media_stream_vec = streams(); - stream_infos->reserve(media_stream_vec.size()); - for (size_t i = 0; i < media_stream_vec.size(); ++i) { - stream_infos->push_back(media_stream_vec[i]->info().get()); - } -} - bool MP4Muxer::GetInitRangeStartAndEnd(uint32_t* start, uint32_t* end) { DCHECK(start && end); size_t range_offset = 0; @@ -243,11 +234,15 @@ void MP4Muxer::FireOnMediaStartEvent() { if (!muxer_listener()) return; - std::vector stream_info_vec; - GetStreamInfo(&stream_info_vec); + if (streams().size() > 1) { + LOG(ERROR) << "MuxerListener cannot take more than 1 stream."; + return; + } + DCHECK(!streams().empty()) << "Media started without a stream."; + const uint32_t timescale = segmenter_->GetReferenceTimeScale(); muxer_listener()->OnMediaStart(options(), - stream_info_vec, + *streams().front()->info(), timescale, MuxerListener::kContainerMp4, encryption_key_source() != NULL); diff --git a/packager/media/formats/mp4/mp4_muxer.h b/packager/media/formats/mp4/mp4_muxer.h index a5bc81271f..e9c3d2ff06 100644 --- a/packager/media/formats/mp4/mp4_muxer.h +++ b/packager/media/formats/mp4/mp4_muxer.h @@ -51,9 +51,6 @@ class MP4Muxer : public Muxer { Track* trak, uint32_t track_id); - // Helper functions for events. - void GetStreamInfo(std::vector* stream_infos); - // Gets |start| and |end| initialization range. Returns true if there is an // init range and sets start-end byte-range-spec specified in RFC2616. bool GetInitRangeStartAndEnd(uint32_t* start, uint32_t* end); diff --git a/packager/mpd/base/media_info.proto b/packager/mpd/base/media_info.proto index 76004c0db8..eeffc609c2 100644 --- a/packager/mpd/base/media_info.proto +++ b/packager/mpd/base/media_info.proto @@ -77,9 +77,12 @@ message MediaInfo { } optional uint32 bandwidth = 1; - repeated VideoInfo video_info = 2; - repeated AudioInfo audio_info = 3; - repeated TextInfo text_info = 4; + + // Note that DASH IOP v3.0 explicitly mentions that a segment should only + // have one {video, audio, text} track. + optional VideoInfo video_info = 2; + optional AudioInfo audio_info = 3; + optional TextInfo text_info = 4; repeated ContentProtectionXml content_protections = 5; // This is the reference time scale if there are multiple VideoInfo and/or diff --git a/packager/mpd/base/mpd_builder.cc b/packager/mpd/base/mpd_builder.cc index 18c4991b71..b790b85c48 100644 --- a/packager/mpd/base/mpd_builder.cc +++ b/packager/mpd/base/mpd_builder.cc @@ -119,12 +119,12 @@ uint32_t GetTimeScale(const MediaInfo& media_info) { return media_info.reference_time_scale(); } - if (media_info.video_info_size() > 0) { - return media_info.video_info(0).time_scale(); + if (media_info.has_video_info()) { + return media_info.video_info().time_scale(); } - if (media_info.audio_info_size() > 0) { - return media_info.audio_info(0).time_scale(); + if (media_info.has_audio_info()) { + return media_info.audio_info().time_scale(); } LOG(WARNING) << "No timescale specified, using 1 as timescale."; @@ -186,34 +186,29 @@ std::string MakePathRelative(const std::string& 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 +// Check whether the video info has width and height. +// DASH IOP also requires several other fields for video representations, namely // width, height, framerate, and sar. -bool HasRequiredVideoFields( - ::google::protobuf::RepeatedPtrField 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."; +bool HasRequiredVideoFields(const MediaInfo_VideoInfo& video_info) { + if (!video_info.has_height() || !video_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, !video_info.has_time_scale()) + << "Video info does not contain timescale required for " + "calculating framerate. @frameRate is required for DASH IOP."; + LOG_IF(WARNING, !video_info.has_frame_duration()) + << "Video info does not contain frame duration required " + "for calculating framerate. @frameRate is required for DASH IOP."; + LOG_IF(WARNING, !video_info.has_pixel_width()) + << "Video info does not contain pixel_width to calculate the sample " + "aspect ratio required for DASH IOP."; + LOG_IF(WARNING, !video_info.has_pixel_height()) + << "Video info does not contain pixel_height to calculate the sample " + "aspect ratio required for DASH IOP."; return true; } @@ -528,8 +523,8 @@ Representation* AdaptationSet::AddRepresentation(const MediaInfo& media_info) { // For videos, record the width, height, and the frame rate to calculate the // max {width,height,framerate} required for DASH IOP. - if(media_info.video_info_size() > 0) { - const MediaInfo::VideoInfo& video_info = media_info.video_info(0); + if(media_info.has_video_info()) { + const MediaInfo::VideoInfo& video_info = media_info.video_info(); DCHECK(video_info.has_width()); DCHECK(video_info.has_height()); video_widths_.insert(video_info.width()); @@ -642,8 +637,8 @@ bool Representation::Init() { return false; } - 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_video_info = media_info_.has_video_info(); + const bool has_audio_info = media_info_.has_audio_info(); if (!has_video_info && !has_audio_info) { // This is an error. Segment information can be in AdaptationSet, Period, or @@ -705,8 +700,8 @@ void Representation::AddNewSegment(uint64_t start_time, void Representation::SetSampleDuration(uint32_t sample_duration) { // Assume single video info. - if (media_info_.video_info_size() > 0) - media_info_.mutable_video_info(0)->set_frame_duration(sample_duration); + if (media_info_.has_video_info()) + media_info_.mutable_video_info()->set_frame_duration(sample_duration); } // Uses info in |media_info_| and |content_protection_elements_| to create a @@ -736,8 +731,8 @@ xml::ScopedXmlPtr::type Representation::GetXml() { representation.SetStringAttribute("codecs", codecs_); representation.SetStringAttribute("mimeType", mime_type_); - 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_video_info = media_info_.has_video_info(); + const bool has_audio_info = media_info_.has_audio_info(); if (has_video_info && !representation.AddVideoInfo(media_info_.video_info())) { diff --git a/packager/mpd/base/mpd_builder_unittest.cc b/packager/mpd/base/mpd_builder_unittest.cc index e1e40dd665..525d4d997d 100644 --- a/packager/mpd/base/mpd_builder_unittest.cc +++ b/packager/mpd/base/mpd_builder_unittest.cc @@ -613,7 +613,7 @@ TEST_F(CommonMpdBuilderTest, SetSampleDuration) { EXPECT_TRUE(representation.Init()); representation.SetSampleDuration(2u); EXPECT_EQ(2u, - representation.media_info_.video_info(0).frame_duration()); + representation.media_info_.video_info().frame_duration()); } // Add one video check the output. diff --git a/packager/mpd/base/mpd_utils.cc b/packager/mpd/base/mpd_utils.cc index ab2b3a71ca..1ef94bb504 100644 --- a/packager/mpd/base/mpd_utils.cc +++ b/packager/mpd/base/mpd_utils.cc @@ -14,27 +14,6 @@ #include "packager/mpd/base/media_info.pb.h" #include "packager/mpd/base/xml/scoped_xml_ptr.h" -namespace { - -// Concatenate all the codecs in |repeated_stream_info|. -template -std::string CodecsString(const RepeatedStreamInfoType& repeated_stream_info) { - std::string codecs; - for (int i = 0; i < repeated_stream_info.size(); ++i) { - codecs.append(repeated_stream_info.Get(i).codec()); - codecs.append(","); - } - - if (!codecs.empty()) { - DCHECK_EQ(codecs[codecs.size() - 1], ','); - codecs.resize(codecs.size() - 1); // Cut off ',' at the end. - } - - return codecs; -} - -} // namespace - namespace edash_packager { bool HasVODOnlyFields(const MediaInfo& media_info) { @@ -63,20 +42,20 @@ void RemoveDuplicateAttributes( } std::string GetCodecs(const MediaInfo& media_info) { - std::string video_codecs; - if (media_info.video_info_size() > 0) - video_codecs = CodecsString(media_info.video_info()); + std::string video_codec; + if (media_info.has_video_info()) + video_codec = media_info.video_info().codec(); - std::string audio_codecs; - if (media_info.audio_info_size() > 0) - audio_codecs = CodecsString(media_info.audio_info()); + std::string audio_codec; + if (media_info.has_audio_info()) + audio_codec = media_info.audio_info().codec(); - if (!video_codecs.empty() && !audio_codecs.empty()) { - return video_codecs + "," + audio_codecs; - } else if (!video_codecs.empty()) { - return video_codecs; - } else if (!audio_codecs.empty()) { - return audio_codecs; + if (!video_codec.empty() && !audio_codec.empty()) { + return video_codec + "," + audio_codec; + } else if (!video_codec.empty()) { + return video_codec; + } else if (!audio_codec.empty()) { + return audio_codec; } return ""; diff --git a/packager/mpd/base/simple_mpd_notifier.cc b/packager/mpd/base/simple_mpd_notifier.cc index 6b56c09eda..e7288c49db 100644 --- a/packager/mpd/base/simple_mpd_notifier.cc +++ b/packager/mpd/base/simple_mpd_notifier.cc @@ -49,8 +49,8 @@ bool SimpleMpdNotifier::NotifyNewContainer(const MediaInfo& media_info, // TODO(kqyang): Consider adding a new method MpdBuilder::AddRepresentation. // Most of the codes here can be moved inside. std::string lang; - if (media_info.audio_info().size() > 0) { - lang = media_info.audio_info(0).language(); + if (media_info.has_audio_info()) { + lang = media_info.audio_info().language(); } AdaptationSet** adaptation_set = &adaptation_set_map_[content_type][lang]; if (*adaptation_set == NULL) @@ -110,9 +110,9 @@ bool SimpleMpdNotifier::AddContentProtectionElement( SimpleMpdNotifier::ContentType SimpleMpdNotifier::GetContentType( const MediaInfo& media_info) { - const bool has_video = media_info.video_info().size() > 0; - const bool has_audio = media_info.audio_info().size() > 0; - const bool has_text = media_info.text_info().size() > 0; + const bool has_video = media_info.has_video_info(); + const bool has_audio = media_info.has_audio_info(); + const bool has_text = media_info.has_text_info(); if (MoreThanOneTrue(has_video, has_audio, has_text)) { NOTIMPLEMENTED() << "MediaInfo with more than one stream is not supported."; diff --git a/packager/mpd/base/xml/xml_node.cc b/packager/mpd/base/xml/xml_node.cc index 8735bfd28b..d0167ae551 100644 --- a/packager/mpd/base/xml/xml_node.cc +++ b/packager/mpd/base/xml/xml_node.cc @@ -19,6 +19,8 @@ using edash_packager::xml::XmlNode; using edash_packager::MediaInfo; +typedef edash_packager::MediaInfo::AudioInfo AudioInfo; +typedef edash_packager::MediaInfo::VideoInfo VideoInfo; typedef MediaInfo::ContentProtectionXml ContentProtectionXml; typedef ContentProtectionXml::AttributeNameValuePair AttributeNameValuePair; @@ -322,55 +324,27 @@ RepresentationXmlNode::RepresentationXmlNode() : RepresentationBaseXmlNode("Representation") {} RepresentationXmlNode::~RepresentationXmlNode() {} -bool RepresentationXmlNode::AddVideoInfo( - const RepeatedVideoInfo& repeated_video_info) { - uint32_t width = 0; - uint32_t height = 0; - - // Make sure that all the widths and heights match. - for (int i = 0; i < repeated_video_info.size(); ++i) { - const MediaInfo_VideoInfo& video_info = repeated_video_info.Get(i); - if (video_info.width() == 0 || video_info.height() == 0) - return false; - - if (width == 0) { - width = video_info.width(); - } else if (width != video_info.width()) { - return false; - } - - if (height == 0) { - height = video_info.height(); - } else if (height != video_info.height()) { - return false; - } +bool RepresentationXmlNode::AddVideoInfo(const VideoInfo& video_info) { + if (!video_info.has_width() || !video_info.has_height()) { + LOG(ERROR) << "Missing width or height for adding a video info."; + return false; } - if (width != 0) - SetIntegerAttribute("width", width); - - if (height != 0) - 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); + SetIntegerAttribute("width", video_info.width()); + SetIntegerAttribute("height", video_info.height()); 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; } -bool RepresentationXmlNode::AddAudioInfo( - const RepeatedAudioInfo& repeated_audio_info) { - if (!AddAudioChannelInfo(repeated_audio_info)) +bool RepresentationXmlNode::AddAudioInfo(const AudioInfo& audio_info) { + if (!AddAudioChannelInfo(audio_info)) return false; - AddAudioSamplingRateInfo(repeated_audio_info); - + AddAudioSamplingRateInfo(audio_info); return true; } @@ -466,64 +440,24 @@ bool RepresentationXmlNode::AddLiveOnlyInfo( AddChild(segment_template.PassScopedPtr()); } -// Find all the unique number-of-channels in |repeated_audio_info|, and make -// AudioChannelConfiguration for each number-of-channels. -bool RepresentationXmlNode::AddAudioChannelInfo( - const RepeatedAudioInfo& repeated_audio_info) { - std::set num_channels; - for (int i = 0; i < repeated_audio_info.size(); ++i) { - if (repeated_audio_info.Get(i).has_num_channels()) - num_channels.insert(repeated_audio_info.Get(i).num_channels()); - } +bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) { + const uint32_t num_channels = audio_info.num_channels(); + XmlNode audio_channel_config("AudioChannelConfiguration"); + const char kAudioChannelConfigScheme[] = + "urn:mpeg:dash:23003:3:audio_channel_configuration:2011"; + audio_channel_config.SetStringAttribute("schemeIdUri", + kAudioChannelConfigScheme); + audio_channel_config.SetIntegerAttribute("value", num_channels); - std::set::const_iterator num_channels_it = num_channels.begin(); - for (; num_channels_it != num_channels.end(); ++num_channels_it) { - XmlNode audio_channel_config("AudioChannelConfiguration"); - - const char kAudioChannelConfigScheme[] = - "urn:mpeg:dash:23003:3:audio_channel_configuration:2011"; - audio_channel_config.SetStringAttribute("schemeIdUri", - kAudioChannelConfigScheme); - audio_channel_config.SetIntegerAttribute("value", *num_channels_it); - - if (!AddChild(audio_channel_config.PassScopedPtr())) - return false; - } - - return true; + return AddChild(audio_channel_config.PassScopedPtr()); } // MPD expects one number for sampling frequency, or if it is a range it should // be space separated. void RepresentationXmlNode::AddAudioSamplingRateInfo( - const RepeatedAudioInfo& repeated_audio_info) { - bool has_sampling_frequency = false; - uint32_t min_sampling_frequency = std::numeric_limits::max(); - uint32_t max_sampling_frequency = 0; - - for (int i = 0; i < repeated_audio_info.size(); ++i) { - const MediaInfo_AudioInfo &audio_info = repeated_audio_info.Get(i); - if (audio_info.has_sampling_frequency()) { - has_sampling_frequency = true; - const uint32_t sampling_frequency = audio_info.sampling_frequency(); - if (sampling_frequency < min_sampling_frequency) - min_sampling_frequency = sampling_frequency; - - if (sampling_frequency > max_sampling_frequency) - max_sampling_frequency = sampling_frequency; - } - } - - if (has_sampling_frequency) { - if (min_sampling_frequency == max_sampling_frequency) { - SetIntegerAttribute("audioSamplingRate", min_sampling_frequency); - } else { - std::string sample_rate_string = - base::UintToString(min_sampling_frequency) + " " + - base::UintToString(max_sampling_frequency); - SetStringAttribute("audioSamplingRate", sample_rate_string); - } - } + const AudioInfo& audio_info) { + if (audio_info.has_sampling_frequency()) + SetIntegerAttribute("audioSamplingRate", audio_info.sampling_frequency()); } } // namespace xml diff --git a/packager/mpd/base/xml/xml_node.h b/packager/mpd/base/xml/xml_node.h index cce2310d13..56f025c7e2 100644 --- a/packager/mpd/base/xml/xml_node.h +++ b/packager/mpd/base/xml/xml_node.h @@ -124,28 +124,20 @@ class AdaptationSetXmlNode : public RepresentationBaseXmlNode { /// RepresentationType in MPD. class RepresentationXmlNode : public RepresentationBaseXmlNode { public: - typedef ::google::protobuf::RepeatedPtrField - RepeatedVideoInfo; - - typedef ::google::protobuf::RepeatedPtrField - RepeatedAudioInfo; - RepresentationXmlNode(); virtual ~RepresentationXmlNode(); /// Adds video metadata to the MPD. - /// @param repeated_video_info constains the VideoInfos for Representation. - /// Input of size 0 is valid. + /// @param video_info constains the VideoInfo for a Representation. /// @return true if successfully set attributes and children elements (if /// applicable), false otherwise. - bool AddVideoInfo(const RepeatedVideoInfo& repeated_video_info); + bool AddVideoInfo(const MediaInfo::VideoInfo& video_info); /// Adds audio metadata to the MPD. - /// @param repeated_audio_info constains the AudioInfos for Representation. - /// Input of size 0 is valid. + /// @param audio_info constains the AudioInfos for a Representation. /// @return true if successfully set attributes and children elements (if /// applicable), false otherwise. - bool AddAudioInfo(const RepeatedAudioInfo& repeated_audio_info); + bool AddAudioInfo(const MediaInfo::AudioInfo& audio_info); /// Adds fields that are specific to VOD. This ignores @a media_info fields /// for Live. @@ -160,13 +152,12 @@ class RepresentationXmlNode : public RepresentationBaseXmlNode { uint32_t start_number); private: - // Add AudioChannelConfiguration elements. This will add multiple - // AudioChannelConfiguration if @a repeated_audio_info contains multiple - // distinct channel configs (e.g. 2 channels and 6 channels adds 2 elements). - bool AddAudioChannelInfo(const RepeatedAudioInfo& repeated_audio_info); + // Add AudioChannelConfiguration element. Note that it is a required element + // for audio Representations. + bool AddAudioChannelInfo(const MediaInfo::AudioInfo& audio_info); - // Add audioSamplingRate attribute to this element. - void AddAudioSamplingRateInfo(const RepeatedAudioInfo& repeated_audio_info); + // Add audioSamplingRate attribute to this element, if present. + void AddAudioSamplingRateInfo(const MediaInfo::AudioInfo& audio_info); DISALLOW_COPY_AND_ASSIGN(RepresentationXmlNode); }; diff --git a/packager/mpd/util/mpd_writer.cc b/packager/mpd/util/mpd_writer.cc index 1ec35ae462..abf11e45c5 100644 --- a/packager/mpd/util/mpd_writer.cc +++ b/packager/mpd/util/mpd_writer.cc @@ -17,17 +17,6 @@ using edash_packager::media::File; namespace edash_packager { namespace { -bool HasVideo(const MediaInfo& media_info) { - return media_info.video_info().size() > 0; -} - -bool HasAudio(const MediaInfo& media_info) { - return media_info.audio_info().size() > 0; -} - -bool HasText(const MediaInfo& media_info) { - return media_info.text_info().size() > 0; -} // On entry set |has_video|, |has_audio|, and |has_text| to false. // On success, return true and set appropriate |has_*| variables. Otherwise @@ -48,9 +37,9 @@ bool HasVideoAudioText(const std::list& media_infos, it != media_infos.end(); ++it) { const MediaInfo& media_info = *it; - const bool media_info_has_video = HasVideo(media_info); - const bool media_info_has_audio = HasAudio(media_info); - const bool media_info_has_text = HasText(media_info); + const bool media_info_has_video = media_info.has_video_info(); + const bool media_info_has_audio = media_info.has_audio_info(); + const bool media_info_has_text = media_info.has_text_info(); if (MoreThanOneTrue( media_info_has_video, media_info_has_audio, media_info_has_text)) { @@ -101,17 +90,18 @@ bool SetMediaInfosToMpdBuilder(const std::list& media_infos, it != media_infos.end(); ++it) { const MediaInfo& media_info = *it; - DCHECK(OnlyOneTrue( - HasVideo(media_info), HasAudio(media_info), HasText(media_info))); + DCHECK(OnlyOneTrue(media_info.has_video_info(), + media_info.has_audio_info(), + media_info.has_text_info())); std::string lang; AdaptationSet** adaptation_set = NULL; - if (HasVideo(media_info)) { + if (media_info.has_video_info()) { adaptation_set = &map["video"][lang]; - } else if (HasAudio(media_info)) { - lang = media_info.audio_info(0).language(); + } else if (media_info.has_audio_info()) { + lang = media_info.audio_info().language(); adaptation_set = &map["audio"][lang]; - } else if (HasText(media_info)) { + } else if (media_info.has_text_info()) { adaptation_set = &map["text"][lang]; } if (!*adaptation_set) {