blob: 3fe9b67c51e28fc6368820dd5a75b8578f8d3e94 [file] [log] [blame]
/*
* Copyright (c) 2012 The WebM project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "adaptation_set.h"
#include <cstdio>
#include <set>
#include <utility>
#include "dash_model.h"
#include "indent.h"
#include "representation.h"
#include "webm_constants.h"
#include "webm_file.h"
using std::string;
using std::vector;
using webm_tools::Indent;
using webm_tools::kNanosecondsPerSecond;
using webm_tools::WebMFile;
namespace webm_dash {
typedef vector<Representation*>::iterator RepresentationIterator;
typedef vector<Representation*>::const_iterator RepresentationConstIterator;
AdaptationSet::AdaptationSet(const string& id, const DashModel& dash)
: codec_(),
dash_model_(dash),
id_(id),
lang_(),
mimetype_(),
profile_(),
duration_(0.0) {
}
AdaptationSet::~AdaptationSet() {
for (RepresentationIterator iter = representations_.begin();
iter != representations_.end();
++iter) {
const Representation* const r = *iter;
delete r;
}
}
bool AdaptationSet::Init() {
RepresentationIterator iter;
for (iter = representations_.begin();
iter != representations_.end();
++iter) {
if ((*iter)->SetWebMFile() == false)
return false;
}
// Check to see if all the codecs match.
iter = representations_.begin();
if (!(*iter)->webm_file())
return false;
codec_ = (*iter)->webm_file()->GetCodec();
mimetype_ = (*iter)->webm_file()->GetMimeType();
for (iter = representations_.begin() + 1;
iter != representations_.end();
++iter) {
const Representation* const rep = *iter;
const WebMFile* const webm = rep->webm_file();
if (!webm)
return false;
if (webm->GetCodec() != codec_) {
fprintf(stderr, "Representation id:%s codec:%s", rep->id().c_str(),
webm->GetCodec().c_str());
fprintf(stderr, " does not match in AdaptationSet id:%s\n", id_.c_str());
return false;
}
}
// Get max duration. All of the streams should have a duration that is close.
// TODO(fgalligan): Add a check to make sure the durations are close.
for (iter = representations_.begin();
iter != representations_.end();
++iter) {
const WebMFile* const webm = (*iter)->webm_file();
if (!webm)
return false;
const double duration =
webm->GetDurationNanoseconds() / kNanosecondsPerSecond;
if (duration > duration_)
duration_ = duration;
}
// Check that Representation ids do not match. Using set to check if
// the string has been inserted.
std::set<string> test_set;
std::pair<std::set<string>::iterator, bool> ret;
for (iter = representations_.begin();
iter != representations_.end();
++iter) {
string id = (*iter)->id();
ret = test_set.insert(id);
if (ret.second == false) {
fprintf(stderr,
"Representation id:%s is duplicate in AdaptationSet id:%s\n",
id.c_str(), id_.c_str());
return false;
}
}
const int sample_rate = MatchingAudioSamplingRate();
const int width = MatchingWidth();
const int height = MatchingHeight();
for (iter = representations_.begin();
iter != representations_.end();
++iter) {
if (sample_rate)
(*iter)->set_output_audio_sample_rate(false);
if (width)
(*iter)->set_output_video_width(false);
if (height)
(*iter)->set_output_video_height(false);
}
return true;
}
void AdaptationSet::AddRepresentation() {
char str[128];
snprintf(str, sizeof(str), "%d", static_cast<int>(representations_.size()));
const string id = str;
representations_.push_back(new Representation(id, dash_model_));
}
Representation* AdaptationSet::CurrentRepresentation() const {
RepresentationConstIterator iter_curr(representations_.end());
if (iter_curr == representations_.begin())
return NULL;
--iter_curr;
return *iter_curr;
}
const Representation* AdaptationSet::FindRepresentation(
const string& id) const {
for (RepresentationConstIterator iter = representations_.begin();
iter != representations_.end();
++iter) {
if ((*iter)->id() == id)
return *iter;
}
return NULL;
}
void AdaptationSet::OutputDashManifest(FILE* o, Indent* indent) const {
indent->Adjust(webm_tools::kIncreaseIndent);
fprintf(o, "%s<AdaptationSet id=\"%s\"", indent->indent_str().c_str(),
id_.c_str());
fprintf(o, " mimeType=\"%s\"", mimetype_.c_str());
fprintf(o, " codecs=\"%s\"", codec_.c_str());
if (!lang_.empty())
fprintf(o, " lang=\"%s\"", lang_.c_str());
const int sample_rate = MatchingAudioSamplingRate();
if (sample_rate)
fprintf(o, " audioSamplingRate=\"%d\"", sample_rate);
const int width = MatchingWidth();
if (width)
fprintf(o, " width=\"%d\"", width);
const int height = MatchingHeight();
if (height)
fprintf(o, " height=\"%d\"", height);
const bool alignment = SubsegmentAlignment();
if (alignment) {
fprintf(o, " subsegmentAlignment=\"true\"");
} else if (representations_.size() > 1 &&
profile_ == DashModel::webm_on_demand) {
printf("Warning profile is WebM On-Demand and AdaptationSet id:%s",
id_.c_str());
printf(" does not have subSegmentAlignment.\n");
}
// WebM is only type '1' or '0'.
const bool subsegment_starts_with_SAP = SubsegmentStartsWithSAP();
if (subsegment_starts_with_SAP) {
fprintf(o, " subsegmentStartsWithSAP=\"1\"");
} else if (profile_ == DashModel::webm_on_demand) {
printf("Warning profile is WebM On-Demand and AdaptationSet id:%s",
id_.c_str());
printf(" has subsegments that do not start with SAP.\n");
}
const bool switching = BitstreamSwitching();
if (switching)
fprintf(o, " bitstreamSwitching=\"true\"");
fprintf(o, ">\n");
for (RepresentationConstIterator c_iter = representations_.begin();
c_iter != representations_.end();
++c_iter) {
(*c_iter)->OutputDashManifest(o, indent);
}
fprintf(o, "%s</AdaptationSet>\n", indent->indent_str().c_str());
indent->Adjust(webm_tools::kDecreaseIndent);
}
bool AdaptationSet::BitstreamSwitching() const {
if (representations_.size() < 2)
return false;
RepresentationConstIterator golden_iter(representations_.begin());
for (RepresentationConstIterator iter = representations_.begin() + 1;
iter != representations_.end();
++iter) {
const Representation* const r = *iter;
if (!r->BitstreamSwitching(**golden_iter))
return false;
}
return true;
}
int AdaptationSet::MatchingAudioSamplingRate() const {
if (representations_.empty())
return 0;
const int first_rate = (*representations_.begin())->GetAudioSampleRate();
if (!first_rate)
return 0;
for (RepresentationConstIterator iter = representations_.begin() + 1;
iter != representations_.end();
++iter) {
if (first_rate != (*iter)->GetAudioSampleRate())
return 0;
}
return first_rate;
}
int AdaptationSet::MatchingHeight() const {
if (representations_.empty())
return 0;
const int first_height = (*representations_.begin())->GetVideoHeight();
if (!first_height)
return 0;
for (RepresentationConstIterator iter = representations_.begin() + 1;
iter != representations_.end();
++iter) {
if (first_height != (*iter)->GetVideoHeight())
return 0;
}
return first_height;
}
int AdaptationSet::MatchingWidth() const {
if (representations_.empty())
return 0;
const int first_width = (*representations_.begin())->GetVideoWidth();
if (!first_width)
return 0;
for (RepresentationConstIterator iter = representations_.begin() + 1;
iter != representations_.end();
++iter) {
if (first_width != (*iter)->GetVideoWidth())
return 0;
}
return first_width;
}
bool AdaptationSet::SubsegmentAlignment() const {
if (representations_.size() < 2)
return false;
RepresentationConstIterator golden_iter(representations_.begin());
for (RepresentationConstIterator iter = representations_.begin() + 1;
iter != representations_.end();
++iter) {
const Representation* const r = *iter;
if (!r->CheckCuesAlignment(**golden_iter))
return false;
}
return true;
}
bool AdaptationSet::SubsegmentStartsWithSAP() const {
if (representations_.empty())
return false;
for (RepresentationConstIterator iter = representations_.begin();
iter != representations_.end();
++iter) {
const Representation* const r = *iter;
if (!r->SubsegmentStartsWithSAP())
return false;
}
return true;
}
} // namespace webm_dash