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
This commit is contained in:
Rintaro Kuroiwa 2015-06-09 16:58:32 -07:00
parent a02e3b60df
commit 2cf673055c
18 changed files with 134 additions and 263 deletions

View File

@ -34,13 +34,13 @@ void MpdNotifyMuxerListener::SetContentProtectionSchemeIdUri(
void MpdNotifyMuxerListener::OnMediaStart( void MpdNotifyMuxerListener::OnMediaStart(
const MuxerOptions& muxer_options, const MuxerOptions& muxer_options,
const std::vector<StreamInfo*>& stream_infos, const StreamInfo& stream_info,
uint32_t time_scale, uint32_t time_scale,
ContainerType container_type, ContainerType container_type,
bool is_encrypted) { bool is_encrypted) {
scoped_ptr<MediaInfo> media_info(new MediaInfo()); scoped_ptr<MediaInfo> media_info(new MediaInfo());
if (!internal::GenerateMediaInfo(muxer_options, if (!internal::GenerateMediaInfo(muxer_options,
stream_infos, stream_info,
time_scale, time_scale,
container_type, container_type,
media_info.get())) { media_info.get())) {

View File

@ -37,7 +37,7 @@ class MpdNotifyMuxerListener : public MuxerListener {
/// @name MuxerListener implementation overrides. /// @name MuxerListener implementation overrides.
/// @{ /// @{
virtual void OnMediaStart(const MuxerOptions& muxer_options, virtual void OnMediaStart(const MuxerOptions& muxer_options,
const std::vector<StreamInfo*>& stream_infos, const StreamInfo& stream_info,
uint32_t time_scale, uint32_t time_scale,
ContainerType container_type, ContainerType container_type,
bool is_encrypted) OVERRIDE; bool is_encrypted) OVERRIDE;

View File

@ -37,10 +37,10 @@ class MuxerListener {
// Called when muxing starts. This event happens before any other events. // Called when muxing starts. This event happens before any other events.
// For MPEG DASH Live profile, the initialization segment information is // For MPEG DASH Live profile, the initialization segment information is
// available from StreamInfo. // available from StreamInfo.
// |time_scale| is a reference time scale regardless of the time scale(s) // |time_scale| is a reference time scale that overrides the time scale
// specified in |stream_infos|. // specified in |stream_info|.
virtual void OnMediaStart(const MuxerOptions& muxer_options, virtual void OnMediaStart(const MuxerOptions& muxer_options,
const std::vector<StreamInfo*>& stream_infos, const StreamInfo& stream_info,
uint32_t time_scale, uint32_t time_scale,
ContainerType container_type, ContainerType container_type,
bool is_encrypted) = 0; bool is_encrypted) = 0;

View File

@ -80,7 +80,7 @@ void AddVideoInfo(const VideoStreamInfo* video_stream_info,
MediaInfo* media_info) { MediaInfo* media_info) {
DCHECK(video_stream_info); DCHECK(video_stream_info);
DCHECK(media_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_codec(video_stream_info->codec_string());
video_info->set_width(video_stream_info->width()); video_info->set_width(video_stream_info->width());
video_info->set_height(video_stream_info->height()); video_info->set_height(video_stream_info->height());
@ -96,7 +96,7 @@ void AddAudioInfo(const AudioStreamInfo* audio_stream_info,
MediaInfo* media_info) { MediaInfo* media_info) {
DCHECK(audio_stream_info); DCHECK(audio_stream_info);
DCHECK(media_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_codec(audio_stream_info->codec_string());
audio_info->set_sampling_frequency(audio_stream_info->sampling_frequency()); audio_info->set_sampling_frequency(audio_stream_info->sampling_frequency());
audio_info->set_time_scale(audio_stream_info->time_scale()); audio_info->set_time_scale(audio_stream_info->time_scale());
@ -114,25 +114,16 @@ void AddAudioInfo(const AudioStreamInfo* audio_stream_info,
} }
} }
void SetMediaInfoStreamInfo(const std::vector<StreamInfo*>& stream_infos, void SetMediaInfoStreamInfo(const StreamInfo& stream_info,
MediaInfo* media_info) { MediaInfo* media_info) {
typedef std::vector<StreamInfo*>::const_iterator StreamInfoIterator; if (stream_info.stream_type() == kStreamAudio) {
for (StreamInfoIterator it = stream_infos.begin(); AddAudioInfo(static_cast<const AudioStreamInfo*>(&stream_info),
it != stream_infos.end();
++it) {
const StreamInfo* stream_info = *it;
if (!stream_info)
continue;
if (stream_info->stream_type() == kStreamAudio) {
AddAudioInfo(static_cast<const AudioStreamInfo*>(stream_info),
media_info); media_info);
} else { } else {
DCHECK_EQ(stream_info->stream_type(), kStreamVideo); DCHECK_EQ(stream_info.stream_type(), kStreamVideo);
AddVideoInfo(static_cast<const VideoStreamInfo*>(stream_info), AddVideoInfo(static_cast<const VideoStreamInfo*>(&stream_info),
media_info); media_info);
} }
}
} }
void SetMediaInfoMuxerOptions(const MuxerOptions& muxer_options, void SetMediaInfoMuxerOptions(const MuxerOptions& muxer_options,
@ -150,14 +141,14 @@ void SetMediaInfoMuxerOptions(const MuxerOptions& muxer_options,
} // namespace } // namespace
bool GenerateMediaInfo(const MuxerOptions& muxer_options, bool GenerateMediaInfo(const MuxerOptions& muxer_options,
const std::vector<StreamInfo*>& stream_infos, const StreamInfo& stream_info,
uint32_t reference_time_scale, uint32_t reference_time_scale,
MuxerListener::ContainerType container_type, MuxerListener::ContainerType container_type,
MediaInfo* media_info) { MediaInfo* media_info) {
DCHECK(media_info); DCHECK(media_info);
SetMediaInfoMuxerOptions(muxer_options, 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); media_info->set_reference_time_scale(reference_time_scale);
SetMediaInfoContainerType(container_type, media_info); SetMediaInfoContainerType(container_type, media_info);
if (muxer_options.bandwidth > 0) if (muxer_options.bandwidth > 0)

View File

@ -28,7 +28,7 @@ namespace internal {
/// @param[out] media_info points to the MediaInfo object to be filled. /// @param[out] media_info points to the MediaInfo object to be filled.
/// @return true on success, false otherwise. /// @return true on success, false otherwise.
bool GenerateMediaInfo(const MuxerOptions& muxer_options, bool GenerateMediaInfo(const MuxerOptions& muxer_options,
const std::vector<StreamInfo*>& stream_infos, const StreamInfo& stream_info,
uint32_t reference_time_scale_, uint32_t reference_time_scale_,
MuxerListener::ContainerType container_type, MuxerListener::ContainerType container_type,
MediaInfo* media_info); MediaInfo* media_info);

View File

@ -31,14 +31,14 @@ void VodMediaInfoDumpMuxerListener::SetContentProtectionSchemeIdUri(
void VodMediaInfoDumpMuxerListener::OnMediaStart( void VodMediaInfoDumpMuxerListener::OnMediaStart(
const MuxerOptions& muxer_options, const MuxerOptions& muxer_options,
const std::vector<StreamInfo*>& stream_infos, const StreamInfo& stream_info,
uint32_t time_scale, uint32_t time_scale,
ContainerType container_type, ContainerType container_type,
bool is_encrypted) { bool is_encrypted) {
DCHECK(muxer_options.single_segment); DCHECK(muxer_options.single_segment);
media_info_.reset(new MediaInfo()); media_info_.reset(new MediaInfo());
if (!internal::GenerateMediaInfo(muxer_options, if (!internal::GenerateMediaInfo(muxer_options,
stream_infos, stream_info,
time_scale, time_scale,
container_type, container_type,
media_info_.get())) { media_info_.get())) {
@ -58,8 +58,8 @@ void VodMediaInfoDumpMuxerListener::OnMediaStart(
void VodMediaInfoDumpMuxerListener::OnSampleDurationReady( void VodMediaInfoDumpMuxerListener::OnSampleDurationReady(
uint32_t sample_duration) { uint32_t sample_duration) {
// Assume one VideoInfo. // Assume one VideoInfo.
if (media_info_->video_info_size() > 0) { if (media_info_->has_video_info()) {
media_info_->mutable_video_info(0)->set_frame_duration(sample_duration); media_info_->mutable_video_info()->set_frame_duration(sample_duration);
} }
} }

View File

@ -37,7 +37,7 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener {
/// @name MuxerListener implementation overrides. /// @name MuxerListener implementation overrides.
/// @{ /// @{
virtual void OnMediaStart(const MuxerOptions& muxer_options, virtual void OnMediaStart(const MuxerOptions& muxer_options,
const std::vector<StreamInfo*>& stream_infos, const StreamInfo& stream_info,
uint32_t time_scale, uint32_t time_scale,
ContainerType container_type, ContainerType container_type,
bool is_encrypted) OVERRIDE; bool is_encrypted) OVERRIDE;

View File

@ -167,13 +167,13 @@ class VodMediaInfoDumpMuxerListenerTest : public ::testing::Test {
} }
void FireOnMediaStartWithDefaultMuxerOptions( void FireOnMediaStartWithDefaultMuxerOptions(
const std::vector<StreamInfo*> stream_infos, const StreamInfo& stream_info,
bool enable_encryption) { bool enable_encryption) {
MuxerOptions muxer_options; MuxerOptions muxer_options;
SetDefaultMuxerOptionsValues(&muxer_options); SetDefaultMuxerOptionsValues(&muxer_options);
const uint32_t kReferenceTimeScale = 1000; const uint32_t kReferenceTimeScale = 1000;
listener_->OnMediaStart(muxer_options, listener_->OnMediaStart(muxer_options,
stream_infos, stream_info,
kReferenceTimeScale, kReferenceTimeScale,
MuxerListener::kContainerMp4, MuxerListener::kContainerMp4,
enable_encryption); enable_encryption);
@ -212,10 +212,8 @@ class VodMediaInfoDumpMuxerListenerTest : public ::testing::Test {
TEST_F(VodMediaInfoDumpMuxerListenerTest, UnencryptedStream_Normal) { TEST_F(VodMediaInfoDumpMuxerListenerTest, UnencryptedStream_Normal) {
scoped_refptr<StreamInfo> stream_info = scoped_refptr<StreamInfo> stream_info =
CreateVideoStreamInfo(GetDefaultVideoStreamInfoParams()); CreateVideoStreamInfo(GetDefaultVideoStreamInfoParams());
std::vector<StreamInfo*> stream_infos;
stream_infos.push_back(stream_info.get());
FireOnMediaStartWithDefaultMuxerOptions(stream_infos, !kEnableEncryption); FireOnMediaStartWithDefaultMuxerOptions(*stream_info, !kEnableEncryption);
OnMediaEndParameters media_end_param = GetDefaultOnMediaEndParams(); OnMediaEndParameters media_end_param = GetDefaultOnMediaEndParams();
FireOnMediaEndWithParams(media_end_param); FireOnMediaEndWithParams(media_end_param);
@ -247,10 +245,8 @@ TEST_F(VodMediaInfoDumpMuxerListenerTest, EncryptedStream_Normal) {
scoped_refptr<StreamInfo> stream_info = scoped_refptr<StreamInfo> stream_info =
CreateVideoStreamInfo(GetDefaultVideoStreamInfoParams()); CreateVideoStreamInfo(GetDefaultVideoStreamInfoParams());
std::vector<StreamInfo*> stream_infos;
stream_infos.push_back(stream_info.get());
FireOnMediaStartWithDefaultMuxerOptions(stream_infos, kEnableEncryption); FireOnMediaStartWithDefaultMuxerOptions(*stream_info, kEnableEncryption);
OnMediaEndParameters media_end_param = GetDefaultOnMediaEndParams(); OnMediaEndParameters media_end_param = GetDefaultOnMediaEndParams();
FireOnMediaEndWithParams(media_end_param); FireOnMediaEndWithParams(media_end_param);

View File

@ -204,15 +204,6 @@ void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info,
sample_description.audio_entries.push_back(audio); sample_description.audio_entries.push_back(audio);
} }
void MP4Muxer::GetStreamInfo(std::vector<StreamInfo*>* stream_infos) {
DCHECK(stream_infos);
const std::vector<MediaStream*>& 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) { bool MP4Muxer::GetInitRangeStartAndEnd(uint32_t* start, uint32_t* end) {
DCHECK(start && end); DCHECK(start && end);
size_t range_offset = 0; size_t range_offset = 0;
@ -243,11 +234,15 @@ void MP4Muxer::FireOnMediaStartEvent() {
if (!muxer_listener()) if (!muxer_listener())
return; return;
std::vector<StreamInfo*> stream_info_vec; if (streams().size() > 1) {
GetStreamInfo(&stream_info_vec); LOG(ERROR) << "MuxerListener cannot take more than 1 stream.";
return;
}
DCHECK(!streams().empty()) << "Media started without a stream.";
const uint32_t timescale = segmenter_->GetReferenceTimeScale(); const uint32_t timescale = segmenter_->GetReferenceTimeScale();
muxer_listener()->OnMediaStart(options(), muxer_listener()->OnMediaStart(options(),
stream_info_vec, *streams().front()->info(),
timescale, timescale,
MuxerListener::kContainerMp4, MuxerListener::kContainerMp4,
encryption_key_source() != NULL); encryption_key_source() != NULL);

View File

@ -51,9 +51,6 @@ class MP4Muxer : public Muxer {
Track* trak, Track* trak,
uint32_t track_id); uint32_t track_id);
// Helper functions for events.
void GetStreamInfo(std::vector<StreamInfo*>* stream_infos);
// Gets |start| and |end| initialization range. Returns true if there is an // Gets |start| and |end| initialization range. Returns true if there is an
// init range and sets start-end byte-range-spec specified in RFC2616. // init range and sets start-end byte-range-spec specified in RFC2616.
bool GetInitRangeStartAndEnd(uint32_t* start, uint32_t* end); bool GetInitRangeStartAndEnd(uint32_t* start, uint32_t* end);

View File

@ -77,9 +77,12 @@ message MediaInfo {
} }
optional uint32 bandwidth = 1; optional uint32 bandwidth = 1;
repeated VideoInfo video_info = 2;
repeated AudioInfo audio_info = 3; // Note that DASH IOP v3.0 explicitly mentions that a segment should only
repeated TextInfo text_info = 4; // 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; repeated ContentProtectionXml content_protections = 5;
// This is the reference time scale if there are multiple VideoInfo and/or // This is the reference time scale if there are multiple VideoInfo and/or

View File

@ -119,12 +119,12 @@ uint32_t GetTimeScale(const MediaInfo& media_info) {
return media_info.reference_time_scale(); return media_info.reference_time_scale();
} }
if (media_info.video_info_size() > 0) { if (media_info.has_video_info()) {
return media_info.video_info(0).time_scale(); return media_info.video_info().time_scale();
} }
if (media_info.audio_info_size() > 0) { if (media_info.has_audio_info()) {
return media_info.audio_info(0).time_scale(); return media_info.audio_info().time_scale();
} }
LOG(WARNING) << "No timescale specified, using 1 as timescale."; 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; return (path.find(mpd_dir) == 0) ? path.substr(mpd_dir.size()) : path;
} }
// Check whether all the video infos have width and height. // Check whether the video info has width and height.
// DASH IOP defines required fields for video representations, namely // DASH IOP also requires several other fields for video representations, namely
// width, height, framerate, and sar. // width, height, framerate, and sar.
bool HasRequiredVideoFields( bool HasRequiredVideoFields(const MediaInfo_VideoInfo& video_info) {
::google::protobuf::RepeatedPtrField<MediaInfo_VideoInfo> video_infos) { if (!video_info.has_height() || !video_info.has_width()) {
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) LOG(ERROR)
<< "Width and height are required fields for generating a valid MPD."; << "Width and height are required fields for generating a valid MPD.";
return false; return false;
} }
// These fields are not required for a valid MPD, but required for DASH IOP // These fields are not required for a valid MPD, but required for DASH IOP
// compliant MPD. MpdBuilder can keep generating MPDs without these fields. // compliant MPD. MpdBuilder can keep generating MPDs without these fields.
LOG_IF(WARNING, !info.has_time_scale()) LOG_IF(WARNING, !video_info.has_time_scale())
<< "Video info does not contain timescale required for " << "Video info does not contain timescale required for "
"calculating framerate. @frameRate is required for DASH IOP."; "calculating framerate. @frameRate is required for DASH IOP.";
LOG_IF(WARNING, !info.has_frame_duration()) LOG_IF(WARNING, !video_info.has_frame_duration())
<< "Video info does not contain frame duration required " << "Video info does not contain frame duration required "
"for calculating framerate. @frameRate is required for DASH IOP."; "for calculating framerate. @frameRate is required for DASH IOP.";
LOG_IF(WARNING, !info.has_pixel_width()) LOG_IF(WARNING, !video_info.has_pixel_width())
<< "Video info does not contain pixel_width to calculate the sample " << "Video info does not contain pixel_width to calculate the sample "
"aspect ratio required for DASH IOP."; "aspect ratio required for DASH IOP.";
LOG_IF(WARNING, !info.has_pixel_height()) LOG_IF(WARNING, !video_info.has_pixel_height())
<< "Video info does not contain pixel_height to calculate the sample " << "Video info does not contain pixel_height to calculate the sample "
"aspect ratio required for DASH IOP."; "aspect ratio required for DASH IOP.";
}
return true; 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 // For videos, record the width, height, and the frame rate to calculate the
// max {width,height,framerate} required for DASH IOP. // max {width,height,framerate} required for DASH IOP.
if(media_info.video_info_size() > 0) { if(media_info.has_video_info()) {
const MediaInfo::VideoInfo& video_info = media_info.video_info(0); const MediaInfo::VideoInfo& video_info = media_info.video_info();
DCHECK(video_info.has_width()); DCHECK(video_info.has_width());
DCHECK(video_info.has_height()); DCHECK(video_info.has_height());
video_widths_.insert(video_info.width()); video_widths_.insert(video_info.width());
@ -642,8 +637,8 @@ bool Representation::Init() {
return false; return false;
} }
const bool has_video_info = media_info_.video_info_size() > 0; const bool has_video_info = media_info_.has_video_info();
const bool has_audio_info = media_info_.audio_info_size() > 0; const bool has_audio_info = media_info_.has_audio_info();
if (!has_video_info && !has_audio_info) { if (!has_video_info && !has_audio_info) {
// This is an error. Segment information can be in AdaptationSet, Period, or // 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) { void Representation::SetSampleDuration(uint32_t sample_duration) {
// Assume single video info. // Assume single video info.
if (media_info_.video_info_size() > 0) if (media_info_.has_video_info())
media_info_.mutable_video_info(0)->set_frame_duration(sample_duration); media_info_.mutable_video_info()->set_frame_duration(sample_duration);
} }
// Uses info in |media_info_| and |content_protection_elements_| to create a // Uses info in |media_info_| and |content_protection_elements_| to create a
@ -736,8 +731,8 @@ xml::ScopedXmlPtr<xmlNode>::type Representation::GetXml() {
representation.SetStringAttribute("codecs", codecs_); representation.SetStringAttribute("codecs", codecs_);
representation.SetStringAttribute("mimeType", mime_type_); representation.SetStringAttribute("mimeType", mime_type_);
const bool has_video_info = media_info_.video_info_size() > 0; const bool has_video_info = media_info_.has_video_info();
const bool has_audio_info = media_info_.audio_info_size() > 0; const bool has_audio_info = media_info_.has_audio_info();
if (has_video_info && if (has_video_info &&
!representation.AddVideoInfo(media_info_.video_info())) { !representation.AddVideoInfo(media_info_.video_info())) {

View File

@ -613,7 +613,7 @@ TEST_F(CommonMpdBuilderTest, SetSampleDuration) {
EXPECT_TRUE(representation.Init()); EXPECT_TRUE(representation.Init());
representation.SetSampleDuration(2u); representation.SetSampleDuration(2u);
EXPECT_EQ(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. // Add one video check the output.

View File

@ -14,27 +14,6 @@
#include "packager/mpd/base/media_info.pb.h" #include "packager/mpd/base/media_info.pb.h"
#include "packager/mpd/base/xml/scoped_xml_ptr.h" #include "packager/mpd/base/xml/scoped_xml_ptr.h"
namespace {
// Concatenate all the codecs in |repeated_stream_info|.
template <typename RepeatedStreamInfoType>
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 { namespace edash_packager {
bool HasVODOnlyFields(const MediaInfo& media_info) { bool HasVODOnlyFields(const MediaInfo& media_info) {
@ -63,20 +42,20 @@ void RemoveDuplicateAttributes(
} }
std::string GetCodecs(const MediaInfo& media_info) { std::string GetCodecs(const MediaInfo& media_info) {
std::string video_codecs; std::string video_codec;
if (media_info.video_info_size() > 0) if (media_info.has_video_info())
video_codecs = CodecsString(media_info.video_info()); video_codec = media_info.video_info().codec();
std::string audio_codecs; std::string audio_codec;
if (media_info.audio_info_size() > 0) if (media_info.has_audio_info())
audio_codecs = CodecsString(media_info.audio_info()); audio_codec = media_info.audio_info().codec();
if (!video_codecs.empty() && !audio_codecs.empty()) { if (!video_codec.empty() && !audio_codec.empty()) {
return video_codecs + "," + audio_codecs; return video_codec + "," + audio_codec;
} else if (!video_codecs.empty()) { } else if (!video_codec.empty()) {
return video_codecs; return video_codec;
} else if (!audio_codecs.empty()) { } else if (!audio_codec.empty()) {
return audio_codecs; return audio_codec;
} }
return ""; return "";

View File

@ -49,8 +49,8 @@ bool SimpleMpdNotifier::NotifyNewContainer(const MediaInfo& media_info,
// TODO(kqyang): Consider adding a new method MpdBuilder::AddRepresentation. // TODO(kqyang): Consider adding a new method MpdBuilder::AddRepresentation.
// Most of the codes here can be moved inside. // Most of the codes here can be moved inside.
std::string lang; std::string lang;
if (media_info.audio_info().size() > 0) { if (media_info.has_audio_info()) {
lang = media_info.audio_info(0).language(); lang = media_info.audio_info().language();
} }
AdaptationSet** adaptation_set = &adaptation_set_map_[content_type][lang]; AdaptationSet** adaptation_set = &adaptation_set_map_[content_type][lang];
if (*adaptation_set == NULL) if (*adaptation_set == NULL)
@ -110,9 +110,9 @@ bool SimpleMpdNotifier::AddContentProtectionElement(
SimpleMpdNotifier::ContentType SimpleMpdNotifier::GetContentType( SimpleMpdNotifier::ContentType SimpleMpdNotifier::GetContentType(
const MediaInfo& media_info) { const MediaInfo& media_info) {
const bool has_video = media_info.video_info().size() > 0; const bool has_video = media_info.has_video_info();
const bool has_audio = media_info.audio_info().size() > 0; const bool has_audio = media_info.has_audio_info();
const bool has_text = media_info.text_info().size() > 0; const bool has_text = media_info.has_text_info();
if (MoreThanOneTrue(has_video, has_audio, has_text)) { if (MoreThanOneTrue(has_video, has_audio, has_text)) {
NOTIMPLEMENTED() << "MediaInfo with more than one stream is not supported."; NOTIMPLEMENTED() << "MediaInfo with more than one stream is not supported.";

View File

@ -19,6 +19,8 @@
using edash_packager::xml::XmlNode; using edash_packager::xml::XmlNode;
using edash_packager::MediaInfo; using edash_packager::MediaInfo;
typedef edash_packager::MediaInfo::AudioInfo AudioInfo;
typedef edash_packager::MediaInfo::VideoInfo VideoInfo;
typedef MediaInfo::ContentProtectionXml ContentProtectionXml; typedef MediaInfo::ContentProtectionXml ContentProtectionXml;
typedef ContentProtectionXml::AttributeNameValuePair AttributeNameValuePair; typedef ContentProtectionXml::AttributeNameValuePair AttributeNameValuePair;
@ -322,55 +324,27 @@ RepresentationXmlNode::RepresentationXmlNode()
: RepresentationBaseXmlNode("Representation") {} : RepresentationBaseXmlNode("Representation") {}
RepresentationXmlNode::~RepresentationXmlNode() {} RepresentationXmlNode::~RepresentationXmlNode() {}
bool RepresentationXmlNode::AddVideoInfo( bool RepresentationXmlNode::AddVideoInfo(const VideoInfo& video_info) {
const RepeatedVideoInfo& repeated_video_info) { if (!video_info.has_width() || !video_info.has_height()) {
uint32_t width = 0; LOG(ERROR) << "Missing width or height for adding a video info.";
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; return false;
} }
if (height == 0) { SetIntegerAttribute("width", video_info.width());
height = video_info.height(); SetIntegerAttribute("height", video_info.height());
} else if (height != video_info.height()) {
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);
SetStringAttribute("frameRate", SetStringAttribute("frameRate",
base::IntToString(video_info.time_scale()) + "/" + base::IntToString(video_info.time_scale()) + "/" +
base::IntToString(video_info.frame_duration())); base::IntToString(video_info.frame_duration()));
SetStringAttribute("sar", base::IntToString(video_info.pixel_width()) + ":" + SetStringAttribute("sar", base::IntToString(video_info.pixel_width()) + ":" +
base::IntToString(video_info.pixel_height())); base::IntToString(video_info.pixel_height()));
return true; return true;
} }
bool RepresentationXmlNode::AddAudioInfo( bool RepresentationXmlNode::AddAudioInfo(const AudioInfo& audio_info) {
const RepeatedAudioInfo& repeated_audio_info) { if (!AddAudioChannelInfo(audio_info))
if (!AddAudioChannelInfo(repeated_audio_info))
return false; return false;
AddAudioSamplingRateInfo(repeated_audio_info); AddAudioSamplingRateInfo(audio_info);
return true; return true;
} }
@ -466,64 +440,24 @@ bool RepresentationXmlNode::AddLiveOnlyInfo(
AddChild(segment_template.PassScopedPtr()); AddChild(segment_template.PassScopedPtr());
} }
// Find all the unique number-of-channels in |repeated_audio_info|, and make bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) {
// AudioChannelConfiguration for each number-of-channels. const uint32_t num_channels = audio_info.num_channels();
bool RepresentationXmlNode::AddAudioChannelInfo(
const RepeatedAudioInfo& repeated_audio_info) {
std::set<uint32_t> 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());
}
std::set<uint32_t>::const_iterator num_channels_it = num_channels.begin();
for (; num_channels_it != num_channels.end(); ++num_channels_it) {
XmlNode audio_channel_config("AudioChannelConfiguration"); XmlNode audio_channel_config("AudioChannelConfiguration");
const char kAudioChannelConfigScheme[] = const char kAudioChannelConfigScheme[] =
"urn:mpeg:dash:23003:3:audio_channel_configuration:2011"; "urn:mpeg:dash:23003:3:audio_channel_configuration:2011";
audio_channel_config.SetStringAttribute("schemeIdUri", audio_channel_config.SetStringAttribute("schemeIdUri",
kAudioChannelConfigScheme); kAudioChannelConfigScheme);
audio_channel_config.SetIntegerAttribute("value", *num_channels_it); audio_channel_config.SetIntegerAttribute("value", num_channels);
if (!AddChild(audio_channel_config.PassScopedPtr())) return AddChild(audio_channel_config.PassScopedPtr());
return false;
}
return true;
} }
// MPD expects one number for sampling frequency, or if it is a range it should // MPD expects one number for sampling frequency, or if it is a range it should
// be space separated. // be space separated.
void RepresentationXmlNode::AddAudioSamplingRateInfo( void RepresentationXmlNode::AddAudioSamplingRateInfo(
const RepeatedAudioInfo& repeated_audio_info) { const AudioInfo& audio_info) {
bool has_sampling_frequency = false; if (audio_info.has_sampling_frequency())
uint32_t min_sampling_frequency = std::numeric_limits<uint32_t>::max(); SetIntegerAttribute("audioSamplingRate", audio_info.sampling_frequency());
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);
}
}
} }
} // namespace xml } // namespace xml

View File

@ -124,28 +124,20 @@ class AdaptationSetXmlNode : public RepresentationBaseXmlNode {
/// RepresentationType in MPD. /// RepresentationType in MPD.
class RepresentationXmlNode : public RepresentationBaseXmlNode { class RepresentationXmlNode : public RepresentationBaseXmlNode {
public: public:
typedef ::google::protobuf::RepeatedPtrField<MediaInfo_VideoInfo>
RepeatedVideoInfo;
typedef ::google::protobuf::RepeatedPtrField<MediaInfo_AudioInfo>
RepeatedAudioInfo;
RepresentationXmlNode(); RepresentationXmlNode();
virtual ~RepresentationXmlNode(); virtual ~RepresentationXmlNode();
/// Adds video metadata to the MPD. /// Adds video metadata to the MPD.
/// @param repeated_video_info constains the VideoInfos for Representation. /// @param video_info constains the VideoInfo for a Representation.
/// Input of size 0 is valid.
/// @return true if successfully set attributes and children elements (if /// @return true if successfully set attributes and children elements (if
/// applicable), false otherwise. /// applicable), false otherwise.
bool AddVideoInfo(const RepeatedVideoInfo& repeated_video_info); bool AddVideoInfo(const MediaInfo::VideoInfo& video_info);
/// Adds audio metadata to the MPD. /// Adds audio metadata to the MPD.
/// @param repeated_audio_info constains the AudioInfos for Representation. /// @param audio_info constains the AudioInfos for a Representation.
/// Input of size 0 is valid.
/// @return true if successfully set attributes and children elements (if /// @return true if successfully set attributes and children elements (if
/// applicable), false otherwise. /// 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 /// Adds fields that are specific to VOD. This ignores @a media_info fields
/// for Live. /// for Live.
@ -160,13 +152,12 @@ class RepresentationXmlNode : public RepresentationBaseXmlNode {
uint32_t start_number); uint32_t start_number);
private: private:
// Add AudioChannelConfiguration elements. This will add multiple // Add AudioChannelConfiguration element. Note that it is a required element
// AudioChannelConfiguration if @a repeated_audio_info contains multiple // for audio Representations.
// distinct channel configs (e.g. 2 channels and 6 channels adds 2 elements). bool AddAudioChannelInfo(const MediaInfo::AudioInfo& audio_info);
bool AddAudioChannelInfo(const RepeatedAudioInfo& repeated_audio_info);
// Add audioSamplingRate attribute to this element. // Add audioSamplingRate attribute to this element, if present.
void AddAudioSamplingRateInfo(const RepeatedAudioInfo& repeated_audio_info); void AddAudioSamplingRateInfo(const MediaInfo::AudioInfo& audio_info);
DISALLOW_COPY_AND_ASSIGN(RepresentationXmlNode); DISALLOW_COPY_AND_ASSIGN(RepresentationXmlNode);
}; };

View File

@ -17,17 +17,6 @@ using edash_packager::media::File;
namespace edash_packager { namespace edash_packager {
namespace { 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 entry set |has_video|, |has_audio|, and |has_text| to false.
// On success, return true and set appropriate |has_*| variables. Otherwise // On success, return true and set appropriate |has_*| variables. Otherwise
@ -48,9 +37,9 @@ bool HasVideoAudioText(const std::list<MediaInfo>& media_infos,
it != media_infos.end(); it != media_infos.end();
++it) { ++it) {
const MediaInfo& media_info = *it; const MediaInfo& media_info = *it;
const bool media_info_has_video = HasVideo(media_info); const bool media_info_has_video = media_info.has_video_info();
const bool media_info_has_audio = HasAudio(media_info); const bool media_info_has_audio = media_info.has_audio_info();
const bool media_info_has_text = HasText(media_info); const bool media_info_has_text = media_info.has_text_info();
if (MoreThanOneTrue( if (MoreThanOneTrue(
media_info_has_video, media_info_has_audio, media_info_has_text)) { media_info_has_video, media_info_has_audio, media_info_has_text)) {
@ -101,17 +90,18 @@ bool SetMediaInfosToMpdBuilder(const std::list<MediaInfo>& media_infos,
it != media_infos.end(); it != media_infos.end();
++it) { ++it) {
const MediaInfo& media_info = *it; const MediaInfo& media_info = *it;
DCHECK(OnlyOneTrue( DCHECK(OnlyOneTrue(media_info.has_video_info(),
HasVideo(media_info), HasAudio(media_info), HasText(media_info))); media_info.has_audio_info(),
media_info.has_text_info()));
std::string lang; std::string lang;
AdaptationSet** adaptation_set = NULL; AdaptationSet** adaptation_set = NULL;
if (HasVideo(media_info)) { if (media_info.has_video_info()) {
adaptation_set = &map["video"][lang]; adaptation_set = &map["video"][lang];
} else if (HasAudio(media_info)) { } else if (media_info.has_audio_info()) {
lang = media_info.audio_info(0).language(); lang = media_info.audio_info().language();
adaptation_set = &map["audio"][lang]; adaptation_set = &map["audio"][lang];
} else if (HasText(media_info)) { } else if (media_info.has_text_info()) {
adaptation_set = &map["text"][lang]; adaptation_set = &map["text"][lang];
} }
if (!*adaptation_set) { if (!*adaptation_set) {