blob: 5122b3289f4618e1b99e148249858b6b9112f76e [file] [log] [blame] [edit]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* 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 "host/frontend/webrtc/libcommon/utils.h"
#include <functional>
#include <map>
#include <json/json.h>
namespace cuttlefish {
namespace webrtc_streaming {
namespace {
Result<void> ValidateField(const Json::Value& obj, const std::string& type,
const std::string& field_name,
const Json::ValueType& field_type, bool required) {
CF_EXPECT(obj.isObject(), "Expected object with name-value pairs");
if (!obj.isMember(field_name) && !required) {
return {};
}
if (!(obj.isMember(field_name) &&
obj[field_name].isConvertibleTo(field_type))) {
std::string error_msg = "Expected a field named '";
error_msg += field_name + "' of type '";
error_msg += std::to_string(field_type);
error_msg += "'";
if (!type.empty()) {
error_msg += " in message of type '" + type + "'";
}
error_msg += ".";
return CF_ERR(error_msg);
}
return {};
}
template <typename T>
Json::Value ToArray(const std::vector<T>& vec,
std::function<Json::Value(const T&)> to_json) {
Json::Value arr(Json::ValueType::arrayValue);
for (const auto& t : vec) {
arr.append(to_json(t));
}
return arr;
}
} // namespace
Result<void> ValidateJsonObject(
const Json::Value& obj, const std::string& type,
const std::map<std::string, Json::ValueType>& required_fields,
const std::map<std::string, Json::ValueType>& optional_fields) {
for (const auto& field_spec : required_fields) {
CF_EXPECT(
ValidateField(obj, type, field_spec.first, field_spec.second, true));
}
for (const auto& field_spec : optional_fields) {
CF_EXPECT(
ValidateField(obj, type, field_spec.first, field_spec.second, false));
}
return {};
}
Result<std::unique_ptr<webrtc::SessionDescriptionInterface>>
ParseSessionDescription(const std::string& type, const Json::Value& message,
webrtc::SdpType sdp_type) {
CF_EXPECT(ValidateJsonObject(message, type,
{{"sdp", Json::ValueType::stringValue}}));
auto remote_desc_str = message["sdp"].asString();
auto remote_desc =
webrtc::CreateSessionDescription(sdp_type, remote_desc_str);
CF_EXPECT(remote_desc.get(), "Failed to parse sdp.");
return remote_desc;
}
Result<std::unique_ptr<webrtc::IceCandidateInterface>> ParseIceCandidate(
const std::string& type, const Json::Value& message) {
CF_EXPECT(ValidateJsonObject(message, type,
{{"candidate", Json::ValueType::objectValue}}));
auto candidate_json = message["candidate"];
CF_EXPECT(ValidateJsonObject(candidate_json, "ice-candidate/candidate",
{
{"sdpMid", Json::ValueType::stringValue},
{"candidate", Json::ValueType::stringValue},
{"sdpMLineIndex", Json::ValueType::intValue},
}));
auto mid = candidate_json["sdpMid"].asString();
auto candidate_sdp = candidate_json["candidate"].asString();
auto line_index = candidate_json["sdpMLineIndex"].asInt();
auto candidate =
std::unique_ptr<webrtc::IceCandidateInterface>(webrtc::CreateIceCandidate(
mid, line_index, candidate_sdp, nullptr /*error*/));
CF_EXPECT(candidate.get(), "Failed to parse ICE candidate");
return candidate;
}
Result<std::string> ParseError(const std::string& type,
const Json::Value& message) {
CF_EXPECT(ValidateJsonObject(message, type,
{{"error", Json::ValueType::stringValue}}));
return message["error"].asString();
}
Result<std::vector<webrtc::PeerConnectionInterface::IceServer>>
ParseIceServersMessage(const Json::Value& message) {
std::vector<webrtc::PeerConnectionInterface::IceServer> ret;
if (!message.isMember("ice_servers") || !message["ice_servers"].isArray()) {
// The ice_servers field is optional in some messages
LOG(VERBOSE)
<< "ice_servers field not present in json object or not an array";
return ret;
}
auto& servers = message["ice_servers"];
for (const auto& server : servers) {
webrtc::PeerConnectionInterface::IceServer ice_server;
CF_EXPECT(server.isMember("urls") && server["urls"].isArray(),
"ICE server specification missing urls field or not an array: "
<< server.toStyledString());
auto urls = server["urls"];
for (int url_idx = 0; url_idx < urls.size(); url_idx++) {
auto url = urls[url_idx];
CF_EXPECT(url.isString(), "Non string 'urls' field in ice server: "
<< url.toStyledString());
ice_server.urls.push_back(url.asString());
}
if (server.isMember("credential") && server["credential"].isString()) {
ice_server.password = server["credential"].asString();
}
if (server.isMember("username") && server["username"].isString()) {
ice_server.username = server["username"].asString();
}
ret.push_back(ice_server);
}
return ret;
}
Json::Value GenerateIceServersMessage(
const std::vector<webrtc::PeerConnectionInterface::IceServer>&
ice_servers) {
return ToArray<webrtc::PeerConnectionInterface::IceServer>(
ice_servers,
[](const webrtc::PeerConnectionInterface::IceServer& ice_server) {
Json::Value server;
server["urls"] = ToArray<std::string>(
ice_server.urls,
[](const std::string& url) { return Json::Value(url); });
server["credential"] = ice_server.password;
server["username"] = ice_server.username;
return server;
});
}
} // namespace webrtc_streaming
} // namespace cuttlefish