170 lines
6.2 KiB
C++
170 lines
6.2 KiB
C++
/*
|
|
* 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
|