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:
parent
a02e3b60df
commit
2cf673055c
|
@ -34,13 +34,13 @@ void MpdNotifyMuxerListener::SetContentProtectionSchemeIdUri(
|
|||
|
||||
void MpdNotifyMuxerListener::OnMediaStart(
|
||||
const MuxerOptions& muxer_options,
|
||||
const std::vector<StreamInfo*>& stream_infos,
|
||||
const StreamInfo& stream_info,
|
||||
uint32_t time_scale,
|
||||
ContainerType container_type,
|
||||
bool is_encrypted) {
|
||||
scoped_ptr<MediaInfo> media_info(new MediaInfo());
|
||||
if (!internal::GenerateMediaInfo(muxer_options,
|
||||
stream_infos,
|
||||
stream_info,
|
||||
time_scale,
|
||||
container_type,
|
||||
media_info.get())) {
|
||||
|
|
|
@ -37,7 +37,7 @@ class MpdNotifyMuxerListener : public MuxerListener {
|
|||
/// @name MuxerListener implementation overrides.
|
||||
/// @{
|
||||
virtual void OnMediaStart(const MuxerOptions& muxer_options,
|
||||
const std::vector<StreamInfo*>& stream_infos,
|
||||
const StreamInfo& stream_info,
|
||||
uint32_t time_scale,
|
||||
ContainerType container_type,
|
||||
bool is_encrypted) OVERRIDE;
|
||||
|
|
|
@ -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<StreamInfo*>& stream_infos,
|
||||
const StreamInfo& stream_info,
|
||||
uint32_t time_scale,
|
||||
ContainerType container_type,
|
||||
bool is_encrypted) = 0;
|
||||
|
|
|
@ -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,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) {
|
||||
typedef std::vector<StreamInfo*>::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<const AudioStreamInfo*>(stream_info),
|
||||
if (stream_info.stream_type() == kStreamAudio) {
|
||||
AddAudioInfo(static_cast<const AudioStreamInfo*>(&stream_info),
|
||||
media_info);
|
||||
} else {
|
||||
DCHECK_EQ(stream_info->stream_type(), kStreamVideo);
|
||||
AddVideoInfo(static_cast<const VideoStreamInfo*>(stream_info),
|
||||
DCHECK_EQ(stream_info.stream_type(), kStreamVideo);
|
||||
AddVideoInfo(static_cast<const VideoStreamInfo*>(&stream_info),
|
||||
media_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetMediaInfoMuxerOptions(const MuxerOptions& muxer_options,
|
||||
|
@ -150,14 +141,14 @@ void SetMediaInfoMuxerOptions(const MuxerOptions& muxer_options,
|
|||
} // namespace
|
||||
|
||||
bool GenerateMediaInfo(const MuxerOptions& muxer_options,
|
||||
const std::vector<StreamInfo*>& 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)
|
||||
|
|
|
@ -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<StreamInfo*>& stream_infos,
|
||||
const StreamInfo& stream_info,
|
||||
uint32_t reference_time_scale_,
|
||||
MuxerListener::ContainerType container_type,
|
||||
MediaInfo* media_info);
|
||||
|
|
|
@ -31,14 +31,14 @@ void VodMediaInfoDumpMuxerListener::SetContentProtectionSchemeIdUri(
|
|||
|
||||
void VodMediaInfoDumpMuxerListener::OnMediaStart(
|
||||
const MuxerOptions& muxer_options,
|
||||
const std::vector<StreamInfo*>& 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener {
|
|||
/// @name MuxerListener implementation overrides.
|
||||
/// @{
|
||||
virtual void OnMediaStart(const MuxerOptions& muxer_options,
|
||||
const std::vector<StreamInfo*>& stream_infos,
|
||||
const StreamInfo& stream_info,
|
||||
uint32_t time_scale,
|
||||
ContainerType container_type,
|
||||
bool is_encrypted) OVERRIDE;
|
||||
|
|
|
@ -167,13 +167,13 @@ class VodMediaInfoDumpMuxerListenerTest : public ::testing::Test {
|
|||
}
|
||||
|
||||
void FireOnMediaStartWithDefaultMuxerOptions(
|
||||
const std::vector<StreamInfo*> 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<StreamInfo> stream_info =
|
||||
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();
|
||||
FireOnMediaEndWithParams(media_end_param);
|
||||
|
||||
|
@ -247,10 +245,8 @@ TEST_F(VodMediaInfoDumpMuxerListenerTest, EncryptedStream_Normal) {
|
|||
|
||||
scoped_refptr<StreamInfo> stream_info =
|
||||
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();
|
||||
FireOnMediaEndWithParams(media_end_param);
|
||||
|
|
|
@ -204,15 +204,6 @@ void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info,
|
|||
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) {
|
||||
DCHECK(start && end);
|
||||
size_t range_offset = 0;
|
||||
|
@ -243,11 +234,15 @@ void MP4Muxer::FireOnMediaStartEvent() {
|
|||
if (!muxer_listener())
|
||||
return;
|
||||
|
||||
std::vector<StreamInfo*> 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);
|
||||
|
|
|
@ -51,9 +51,6 @@ class MP4Muxer : public Muxer {
|
|||
Track* trak,
|
||||
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
|
||||
// init range and sets start-end byte-range-spec specified in RFC2616.
|
||||
bool GetInitRangeStartAndEnd(uint32_t* start, uint32_t* end);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<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()) {
|
||||
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, !info.has_time_scale())
|
||||
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, !info.has_frame_duration())
|
||||
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, !info.has_pixel_width())
|
||||
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, !info.has_pixel_height())
|
||||
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<xmlNode>::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())) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 <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 {
|
||||
|
||||
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 "";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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()) {
|
||||
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 (height == 0) {
|
||||
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);
|
||||
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<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) {
|
||||
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_it);
|
||||
audio_channel_config.SetIntegerAttribute("value", num_channels);
|
||||
|
||||
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<uint32_t>::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
|
||||
|
|
|
@ -124,28 +124,20 @@ class AdaptationSetXmlNode : public RepresentationBaseXmlNode {
|
|||
/// RepresentationType in MPD.
|
||||
class RepresentationXmlNode : public RepresentationBaseXmlNode {
|
||||
public:
|
||||
typedef ::google::protobuf::RepeatedPtrField<MediaInfo_VideoInfo>
|
||||
RepeatedVideoInfo;
|
||||
|
||||
typedef ::google::protobuf::RepeatedPtrField<MediaInfo_AudioInfo>
|
||||
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);
|
||||
};
|
||||
|
|
|
@ -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<MediaInfo>& 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<MediaInfo>& 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) {
|
||||
|
|
Loading…
Reference in New Issue