blob: 035de69f9f3b98d0fa20854140cad8986be53ee8 [file] [log] [blame]
// Copyright 2016 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/shared/starboard/media/mime_type.h"
#include <algorithm>
#include <iosfwd>
#include <locale>
#include <numeric>
#include <string>
#include <vector>
#include "starboard/common/log.h"
#include "starboard/common/string.h"
namespace starboard {
namespace shared {
namespace starboard {
namespace media {
namespace {
typedef std::vector<std::string> Strings;
MimeType::ParamType GetParamTypeByValue(const std::string& value) {
int count;
int i;
if (SbStringScanF(value.c_str(), "%d%n", &i, &count) == 1 &&
count == value.size()) {
return MimeType::kParamTypeInteger;
}
float f;
std::stringstream buffer(value);
buffer.imbue(std::locale::classic());
buffer >> f;
if (!buffer.fail() && buffer.rdbuf()->in_avail() == 0) {
return MimeType::kParamTypeFloat;
}
if (value == "true" || value == "false") {
return MimeType::kParamTypeBoolean;
}
return MimeType::kParamTypeString;
}
bool ContainsSpace(const std::string& str) {
for (size_t i = 0; i < str.size(); ++i) {
if (isspace(str[i])) {
return true;
}
}
return false;
}
void Trim(std::string* str) {
while (!str->empty() && isspace(*str->begin())) {
str->erase(str->begin());
}
while (!str->empty() && isspace(*str->rbegin())) {
str->resize(str->size() - 1);
}
}
Strings SplitAndTrim(const std::string& str, char ch) {
Strings result;
size_t pos = 0;
for (;;) {
size_t next = str.find(ch, pos);
std::string sub_str = str.substr(pos, next - pos);
Trim(&sub_str);
if (!sub_str.empty()) {
result.push_back(sub_str);
}
if (next == str.npos) {
break;
}
pos = next + 1;
}
return result;
}
const char* ParamTypeToString(MimeType::ParamType param_type) {
switch (param_type) {
case MimeType::kParamTypeInteger:
return "Integer";
case MimeType::kParamTypeFloat:
return "Float";
case MimeType::kParamTypeString:
return "String";
case MimeType::kParamTypeBoolean:
return "Boolean";
default:
SB_NOTREACHED();
return "Unknown";
}
}
} // namespace
const int MimeType::kInvalidParamIndex = -1;
MimeType::MimeType(const std::string& content_type)
: raw_content_type_(content_type), is_valid_(false) {
Strings components = SplitAndTrim(content_type, ';');
if (components.empty()) {
return;
}
// 1. Verify if there is a valid type/subtype in the very beginning.
if (ContainsSpace(components.front())) {
return;
}
std::vector<std::string> type_and_container =
SplitAndTrim(components.front(), '/');
if (type_and_container.size() != 2 || type_and_container[0].empty() ||
type_and_container[1].empty()) {
return;
}
type_ = type_and_container[0];
subtype_ = type_and_container[1];
components.erase(components.begin());
// 2. Verify the parameters have valid formats, we want to be strict here.
bool has_codecs = false;
for (Strings::iterator iter = components.begin(); iter != components.end();
++iter) {
std::vector<std::string> name_and_value = SplitAndTrim(*iter, '=');
// The parameter must be on the format 'name=value' and neither |name| nor
// |value| can be empty. |value| must also not contain '|'.
if (name_and_value.size() != 2 || name_and_value[0].empty() ||
name_and_value[1].empty() ||
name_and_value[1].find('|') != std::string::npos) {
return;
}
Param param;
if (name_and_value[1].size() > 2 && name_and_value[1][0] == '\"' &&
*name_and_value[1].rbegin() == '\"') {
param.type = kParamTypeString;
param.value = name_and_value[1].substr(1, name_and_value[1].size() - 2);
} else {
param.type = GetParamTypeByValue(name_and_value[1]);
param.value = name_and_value[1];
}
param.name = name_and_value[0];
if (param.name == "codecs") {
// There can only be no more than one codecs parameter and it has to be
// the first parameter if it is present.
if (!params_.empty() || has_codecs) {
return;
} else {
has_codecs = true;
}
}
params_.push_back(param);
}
is_valid_ = true;
}
const std::vector<std::string>& MimeType::GetCodecs() const {
if (!codecs_.empty()) {
return codecs_;
}
int codecs_index = GetParamIndexByName("codecs");
if (codecs_index != 0) {
return codecs_;
}
codecs_ = SplitAndTrim(params_[0].value, ',');
return codecs_;
}
int MimeType::GetParamCount() const {
SB_DCHECK(is_valid());
return static_cast<int>(params_.size());
}
MimeType::ParamType MimeType::GetParamType(int index) const {
SB_DCHECK(is_valid());
SB_DCHECK(index < GetParamCount());
return params_[index].type;
}
const std::string& MimeType::GetParamName(int index) const {
SB_DCHECK(is_valid());
SB_DCHECK(index < GetParamCount());
return params_[index].name;
}
int MimeType::GetParamIntValue(int index) const {
SB_DCHECK(is_valid());
SB_DCHECK(index < GetParamCount());
if (GetParamType(index) != kParamTypeInteger) {
return 0;
}
int i;
SbStringScanF(params_[index].value.c_str(), "%d", &i);
return i;
}
float MimeType::GetParamFloatValue(int index) const {
SB_DCHECK(is_valid());
SB_DCHECK(index < GetParamCount());
if (GetParamType(index) != kParamTypeInteger &&
GetParamType(index) != kParamTypeFloat) {
return 0.0f;
}
float f;
std::stringstream buffer(params_[index].value.c_str());
buffer.imbue(std::locale::classic());
buffer >> f;
return f;
}
const std::string& MimeType::GetParamStringValue(int index) const {
SB_DCHECK(is_valid());
SB_DCHECK(index < GetParamCount());
return params_[index].value;
}
bool MimeType::GetParamBoolValue(int index) const {
SB_DCHECK(is_valid());
SB_DCHECK(index < GetParamCount());
if (GetParamType(index) != kParamTypeBoolean) {
return false;
}
return params_[index].value == "true";
}
int MimeType::GetParamIntValue(const char* name, int default_value) const {
int index = GetParamIndexByName(name);
if (index != kInvalidParamIndex) {
return GetParamIntValue(index);
}
return default_value;
}
float MimeType::GetParamFloatValue(const char* name,
float default_value) const {
int index = GetParamIndexByName(name);
if (index != kInvalidParamIndex) {
return GetParamFloatValue(index);
}
return default_value;
}
const std::string& MimeType::GetParamStringValue(
const char* name,
const std::string& default_value) const {
int index = GetParamIndexByName(name);
if (index != kInvalidParamIndex) {
return GetParamStringValue(index);
}
return default_value;
}
bool MimeType::GetParamBoolValue(const char* name, bool default_value) const {
int index = GetParamIndexByName(name);
if (index != kInvalidParamIndex) {
return GetParamBoolValue(index);
}
return default_value;
}
bool MimeType::RegisterBoolParameter(const char* name) {
return RegisterParameter(name, kParamTypeBoolean);
}
bool MimeType::RegisterStringParameter(const char* name,
const std::string& pattern /* = "" */) {
if (!RegisterParameter(name, kParamTypeString)) {
return false;
}
int param_index = GetParamIndexByName(name);
if (param_index == kInvalidParamIndex || pattern.empty()) {
return true;
}
// Compare the parameter value with the provided pattern.
const std::string& param_value = GetParamStringValue(param_index);
bool matches = false;
size_t match_start = 0;
while (!matches) {
match_start = pattern.find(param_value, match_start);
if (match_start == std::string::npos) {
break;
}
size_t match_end = match_start + param_value.length();
matches =
// Matches if the match is at the start of the string or
// if the preceding character is the divider _and_
(match_start <= 0 || pattern[match_start - 1] == '|') &&
// if the end of the match is the end of the pattern or
// if the succeeding character is the divider.
(match_end >= pattern.length() || pattern[match_end] == '|');
match_start = match_end + 1;
}
if (matches) {
return true;
}
SB_LOG(INFO) << "Extended Parameter '" << name << "=" << param_value
<< "' does not match the supplied pattern: '" << pattern << "'";
is_valid_ = false;
return false;
}
int MimeType::GetParamIndexByName(const char* name) const {
for (size_t i = 0; i < params_.size(); ++i) {
if (SbStringCompareNoCase(params_[i].name.c_str(), name) == 0) {
return static_cast<int>(i);
}
}
return kInvalidParamIndex;
}
bool MimeType::RegisterParameter(const char* name, ParamType param_type) {
if (!is_valid()) {
return false;
}
int index = GetParamIndexByName(name);
if (index == kInvalidParamIndex) {
return true;
}
const std::string& param_value = GetParamStringValue(index);
ParamType parsed_type = GetParamType(index);
// Check that the parameter can be returned as the requested type.
// Allowed conversions:
// Any Type -> String, Int -> Float
bool convertible =
param_type == parsed_type || param_type == kParamTypeString ||
(param_type == kParamTypeFloat && parsed_type == kParamTypeInteger);
if (!convertible) {
SB_LOG(INFO) << "Extended Parameter '" << name << "=" << param_value
<< "' can't be converted to " << ParamTypeToString(param_type);
is_valid_ = false;
return false;
}
// All validations succeeded.
return true;
}
} // namespace media
} // namespace starboard
} // namespace shared
} // namespace starboard