Implement SimpleMpdNotifier

SimpleMpdNotifier listens to muxer events and generates MPD file.

Change-Id: I19304cdb9eba65fd01328aa0fd5e6d280cc5714e
This commit is contained in:
KongQun Yang 2014-05-19 14:30:58 -07:00
parent f6a54c289f
commit b0e26ff297
5 changed files with 246 additions and 2 deletions

View File

@ -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);

View File

@ -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.

View File

@ -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<std::string>& 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

View File

@ -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 <map>
#include <string>
#include <vector>
#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<std::string>& 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<std::string> base_urls_;
std::string output_path_;
scoped_ptr<MpdBuilder> mpd_builder_;
base::Lock lock_;
typedef std::map<ContentType, AdaptationSet*> AdaptationSetMap;
AdaptationSetMap adaptation_set_map_;
typedef std::map<uint32, Representation*> RepresentationMap;
RepresentationMap representation_map_;
DISALLOW_COPY_AND_ASSIGN(SimpleMpdNotifier);
};
} // namespace dash_packager
#endif // MPD_BASE_SIMPLE_MPD_NOTIFIER_H_

View File

@ -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',