diff --git a/mpd/base/mpd_builder.h b/mpd/base/mpd_builder.h index 8a3389b70a..647056be57 100644 --- a/mpd/base/mpd_builder.h +++ b/mpd/base/mpd_builder.h @@ -60,6 +60,9 @@ class MpdBuilder { /// @return true on success, false otherwise. bool ToString(std::string* output); + /// @return The mpd type. + MpdType type() { return type_; } + private: bool ToStringImpl(std::string* output); diff --git a/mpd/base/mpd_notifier.h b/mpd/base/mpd_notifier.h index 2483433b2a..77c3ed6352 100644 --- a/mpd/base/mpd_notifier.h +++ b/mpd/base/mpd_notifier.h @@ -11,12 +11,11 @@ #define MPD_BASE_MPD_NOTIFIER_H_ #include "base/basictypes.h" -#include "mpd/base/media_info.pb.h" namespace dash_packager { -class ContentProtectionElement; class MediaInfo; +struct ContentProtectionElement; /// Interface for publish/subscribe publisher class which notifies MpdBuilder /// of media-related events. diff --git a/mpd/base/simple_mpd_notifier.cc b/mpd/base/simple_mpd_notifier.cc new file mode 100644 index 0000000000..e0e3ee4fff --- /dev/null +++ b/mpd/base/simple_mpd_notifier.cc @@ -0,0 +1,166 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include "mpd/base/simple_mpd_notifier.h" + +#include "base/logging.h" +#include "media/file/file.h" +#include "mpd/base/mpd_builder.h" +#include "mpd/base/mpd_utils.h" + +using media::File; + +namespace { +bool MoreThanOneTrue(bool b1, bool b2, bool b3) { + return (b1 && b2) || (b2 && b3) || (b3 && b1); +} + +bool AtLeastOneTrue(bool b1, bool b2, bool b3) { + return (b1 || b2 || b3); +} +} // namespace + +namespace dash_packager { + +SimpleMpdNotifier::SimpleMpdNotifier(const std::vector& base_urls, + const std::string& output_path) + : base_urls_(base_urls), output_path_(output_path) { +} + +SimpleMpdNotifier::~SimpleMpdNotifier() { +} + +bool SimpleMpdNotifier::Init() { + return true; +} + +bool SimpleMpdNotifier::NotifyNewContainer(const MediaInfo& media_info, + uint32* container_id) { + DCHECK(container_id); + + ContentType content_type = GetContentType(media_info); + if (content_type == kUnknown) + return false; + + // Determine whether the media_info is for VOD or live. + bool has_vod_only_fields = HasVODOnlyFields(media_info); + bool has_live_only_fields = HasLiveOnlyFields(media_info); + if (has_vod_only_fields && has_live_only_fields) { + LOG(ERROR) << "MediaInfo with both VOD fields and Live fields is invalid."; + return false; + } + if (!has_vod_only_fields && !has_live_only_fields) { + LOG(ERROR) << "MediaInfo should contain either VOD fields or Live fields."; + return false; + } + MpdBuilder::MpdType mpd_type = + has_vod_only_fields ? MpdBuilder::kStatic : MpdBuilder::kDynamic; + + base::AutoLock auto_lock(lock_); + // TODO(kqyang): Consider adding a new method MpdBuilder::AddRepresentation. + // Most of the codes here can be moved inside. + if (!mpd_builder_) { + mpd_builder_.reset(new MpdBuilder(mpd_type)); + for (size_t i = 0; i < base_urls_.size(); ++i) + mpd_builder_->AddBaseUrl(base_urls_[i]); + } else { + if (mpd_builder_->type() != mpd_type) { + LOG(ERROR) << "Expecting MediaInfo with '" + << (has_vod_only_fields ? "Live" : "VOD") << "' fields."; + return false; + } + } + + AdaptationSet** adaptation_set = &adaptation_set_map_[content_type]; + if (*adaptation_set == NULL) + *adaptation_set = mpd_builder_->AddAdaptationSet(); + + DCHECK(*adaptation_set); + Representation* representation = + (*adaptation_set)->AddRepresentation(media_info); + if (representation == NULL) + return false; + + *container_id = representation->id(); + + if (has_vod_only_fields) + return WriteMpdToFile(); + + DCHECK(!ContainsKey(representation_map_, representation->id())); + representation_map_[representation->id()] = representation; + return true; +} + +bool SimpleMpdNotifier::NotifyNewSegment(uint32 container_id, + uint64 start_time, + uint64 duration) { + base::AutoLock auto_lock(lock_); + + RepresentationMap::iterator it = representation_map_.find(container_id); + if (it == representation_map_.end()) { + LOG(ERROR) << "Unexpected container_id: " << container_id; + return false; + } + if (!it->second->AddNewSegment(start_time, duration)) + return false; + return WriteMpdToFile(); +} + +bool SimpleMpdNotifier::AddContentProtectionElement( + uint32 container_id, + const ContentProtectionElement& content_protection_element) { + NOTIMPLEMENTED(); + return false; +} + +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; + + if (MoreThanOneTrue(has_video, has_audio, has_text)) { + NOTIMPLEMENTED() << "MediaInfo with more than one stream is not supported."; + return kUnknown; + } + if (!AtLeastOneTrue(has_video, has_audio, has_text)) { + LOG(ERROR) << "MediaInfo should contain one audio, video, or text stream."; + return kUnknown; + } + return has_video ? kVideo : (has_audio ? kAudio : kText); +} + +bool SimpleMpdNotifier::WriteMpdToFile() { + CHECK(!output_path_.empty()); + + std::string mpd; + if (!mpd_builder_->ToString(&mpd)) { + LOG(ERROR) << "Failed to write MPD to string."; + return false; + } + + File* file = File::Open(output_path_.c_str(), "w"); + if (!file) { + LOG(ERROR) << "Failed to open file for writing: " << output_path_; + return false; + } + + const char* mpd_char_ptr = mpd.data(); + size_t mpd_bytes_left = mpd.size(); + while (mpd_bytes_left > 0) { + int64 length = file->Write(mpd_char_ptr, mpd_bytes_left); + if (length <= 0) { + LOG(ERROR) << "Failed to write to file '" << output_path_ << "' (" + << length << ")."; + return false; + } + mpd_char_ptr += length; + mpd_bytes_left -= length; + } + return file->Close(); +} + +} // namespace dash_packager diff --git a/mpd/base/simple_mpd_notifier.h b/mpd/base/simple_mpd_notifier.h new file mode 100644 index 0000000000..20bf55fd22 --- /dev/null +++ b/mpd/base/simple_mpd_notifier.h @@ -0,0 +1,73 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#ifndef MPD_BASE_SIMPLE_MPD_NOTIFIER_H_ +#define MPD_BASE_SIMPLE_MPD_NOTIFIER_H_ + +#include +#include +#include + +#include "base/memory/scoped_ptr.h" +#include "base/synchronization/lock.h" +#include "mpd/base/mpd_notifier.h" + +namespace dash_packager { + +class AdaptationSet; +class MpdBuilder; +class Representation; + +/// A simple MpdNotifier implementation which receives muxer listener event and +/// generates an Mpd file. +class SimpleMpdNotifier : public MpdNotifier { + public: + SimpleMpdNotifier(const std::vector& base_urls, + const std::string& output_path); + virtual ~SimpleMpdNotifier(); + + /// @name MpdNotifier implemetation overrides. + /// @{ + virtual bool Init() OVERRIDE; + virtual bool NotifyNewContainer(const MediaInfo& media_info, + uint32* id) OVERRIDE; + virtual bool NotifyNewSegment(uint32 id, + uint64 start_time, + uint64 duration) OVERRIDE; + virtual bool AddContentProtectionElement( + uint32 id, + const ContentProtectionElement& content_protection_element) OVERRIDE; + /// @} + + private: + enum ContentType { + kUnknown, + kVideo, + kAudio, + kText + }; + ContentType GetContentType(const MediaInfo& media_info); + bool WriteMpdToFile(); + + std::vector base_urls_; + std::string output_path_; + + scoped_ptr mpd_builder_; + + base::Lock lock_; + + typedef std::map AdaptationSetMap; + AdaptationSetMap adaptation_set_map_; + + typedef std::map RepresentationMap; + RepresentationMap representation_map_; + + DISALLOW_COPY_AND_ASSIGN(SimpleMpdNotifier); +}; + +} // namespace dash_packager + +#endif // MPD_BASE_SIMPLE_MPD_NOTIFIER_H_ diff --git a/mpd/mpd.gyp b/mpd/mpd.gyp index 4cbc1fae82..f3879361bd 100644 --- a/mpd/mpd.gyp +++ b/mpd/mpd.gyp @@ -44,8 +44,11 @@ 'base/content_protection_element.h', 'base/mpd_builder.cc', 'base/mpd_builder.h', + 'base/mpd_notifier.h', 'base/mpd_utils.cc', 'base/mpd_utils.h', + 'base/simple_mpd_notifier.cc', + 'base/simple_mpd_notifier.h', 'base/xml/scoped_xml_ptr.h', 'base/xml/xml_node.cc', 'base/xml/xml_node.h',