diff options
author | Jordan Bayles <jophba@chromium.org> | 2021-06-04 11:42:08 -0700 |
---|---|---|
committer | Openscreen LUCI CQ <openscreen-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2021-06-04 19:38:37 +0000 |
commit | accf7fd700bff19266b17eb271e3c805a3230352 (patch) | |
tree | dbf63a464dad4321e8b4605e57a06c8dc6ef3ba4 /cast | |
parent | dec3ae516ec228e649869988229b0e48ee8bc269 (diff) | |
download | openscreen-accf7fd700bff19266b17eb271e3c805a3230352.tar.gz |
[Cast Streaming] Cleanup OFFER/ANSWER
This patch finishes handling a TODO around json deserialization for
offer messages, and removes some of the duplicate code in the
json_helpers.h header.
One more followup patch after this: ParseAndValidate* methods be simply
renamed to Parse* methods.
Change-Id: I36db01f1841731793597e5f89e904edb36d027ef
Reviewed-on: https://chromium-review.googlesource.com/c/openscreen/+/2911769
Commit-Queue: Jordan Bayles <jophba@chromium.org>
Reviewed-by: Ryan Keane <rwkeane@google.com>
Diffstat (limited to 'cast')
-rw-r--r-- | cast/standalone_sender/looping_file_cast_agent.cc | 16 | ||||
-rw-r--r-- | cast/streaming/answer_messages.cc | 112 | ||||
-rw-r--r-- | cast/streaming/answer_messages.h | 18 | ||||
-rw-r--r-- | cast/streaming/answer_messages_unittest.cc | 48 | ||||
-rw-r--r-- | cast/streaming/offer_messages.cc | 393 | ||||
-rw-r--r-- | cast/streaming/offer_messages.h | 7 | ||||
-rw-r--r-- | cast/streaming/offer_messages_unittest.cc | 55 | ||||
-rw-r--r-- | cast/streaming/receiver_message.cc | 29 | ||||
-rw-r--r-- | cast/streaming/resolution.cc | 15 | ||||
-rw-r--r-- | cast/streaming/resolution.h | 4 | ||||
-rw-r--r-- | cast/streaming/sender_message.cc | 13 | ||||
-rw-r--r-- | cast/streaming/session_messager.cc | 4 |
12 files changed, 346 insertions, 368 deletions
diff --git a/cast/standalone_sender/looping_file_cast_agent.cc b/cast/standalone_sender/looping_file_cast_agent.cc index 5477c155..e26d4f5e 100644 --- a/cast/standalone_sender/looping_file_cast_agent.cc +++ b/cast/standalone_sender/looping_file_cast_agent.cc @@ -132,8 +132,7 @@ void LoopingFileCastAgent::OnMessage(VirtualConnectionRouter* router, HandleReceiverStatus(payload.value()); } else if (HasType(payload.value(), CastMessageType::kLaunchError)) { std::string reason; - if (!json::ParseAndValidateString(payload.value()[kMessageKeyReason], - &reason)) { + if (!json::TryParseString(payload.value()[kMessageKeyReason], &reason)) { reason = "UNKNOWN"; } OSP_LOG_ERROR @@ -142,8 +141,7 @@ void LoopingFileCastAgent::OnMessage(VirtualConnectionRouter* router, Shutdown(); } else if (HasType(payload.value(), CastMessageType::kInvalidRequest)) { std::string reason; - if (!json::ParseAndValidateString(payload.value()[kMessageKeyReason], - &reason)) { + if (!json::TryParseString(payload.value()[kMessageKeyReason], &reason)) { reason = "UNKNOWN"; } OSP_LOG_ERROR << "Cast Receiver thinks our request is invalid: " @@ -167,8 +165,7 @@ void LoopingFileCastAgent::HandleReceiverStatus(const Json::Value& status) { : Json::Value(); std::string running_app_id; - if (!json::ParseAndValidateString(details[kMessageKeyAppId], - &running_app_id) || + if (!json::TryParseString(details[kMessageKeyAppId], &running_app_id) || running_app_id != GetMirroringAppId()) { // The mirroring app is not running. If it was just stopped, Shutdown() will // tear everything down. If it has been stopped already, Shutdown() is a @@ -178,8 +175,7 @@ void LoopingFileCastAgent::HandleReceiverStatus(const Json::Value& status) { } std::string session_id; - if (!json::ParseAndValidateString(details[kMessageKeySessionId], - &session_id) || + if (!json::TryParseString(details[kMessageKeySessionId], &session_id) || session_id.empty()) { OSP_LOG_ERROR << "Cannot continue: Cast Receiver did not provide a session ID for " @@ -207,8 +203,8 @@ void LoopingFileCastAgent::HandleReceiverStatus(const Json::Value& status) { } std::string message_destination_id; - if (!json::ParseAndValidateString(details[kMessageKeyTransportId], - &message_destination_id) || + if (!json::TryParseString(details[kMessageKeyTransportId], + &message_destination_id) || message_destination_id.empty()) { OSP_LOG_ERROR << "Cannot continue: Cast Receiver did not provide a transport ID for " diff --git a/cast/streaming/answer_messages.cc b/cast/streaming/answer_messages.cc index c59aeff8..20af542f 100644 --- a/cast/streaming/answer_messages.cc +++ b/cast/streaming/answer_messages.cc @@ -9,9 +9,9 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" #include "platform/base/error.h" +#include "util/enum_name_table.h" #include "util/json/json_helpers.h" #include "util/osp_logging.h" - namespace openscreen { namespace cast { @@ -110,35 +110,29 @@ static constexpr char kReceiverRtcpDscp[] = "receiverRtcpDscp"; // RTP extensions (such as adaptive playout delay). static constexpr char kRtpExtensions[] = "rtpExtensions"; +EnumNameTable<AspectRatioConstraint, 2> kAspectRatioConstraintNames{ + {{kScalingReceiver, AspectRatioConstraint::kVariable}, + {kScalingSender, AspectRatioConstraint::kFixed}}}; + Json::Value AspectRatioConstraintToJson(AspectRatioConstraint aspect_ratio) { - switch (aspect_ratio) { - case AspectRatioConstraint::kVariable: - return Json::Value(kScalingReceiver); - case AspectRatioConstraint::kFixed: - default: - return Json::Value(kScalingSender); - } + return Json::Value(GetEnumName(kAspectRatioConstraintNames, aspect_ratio) + .value(kScalingSender)); } -bool AspectRatioConstraintParseAndValidate(const Json::Value& value, - AspectRatioConstraint* out) { - // the aspect ratio constraint is an optional field. - if (!value) { - return true; - } - +bool TryParseAspectRatioConstraint(const Json::Value& value, + AspectRatioConstraint* out) { std::string aspect_ratio; - if (!json::ParseAndValidateString(value, &aspect_ratio)) { + if (!json::TryParseString(value, &aspect_ratio)) { return false; } - if (aspect_ratio == kScalingReceiver) { - *out = AspectRatioConstraint::kVariable; - return true; - } else if (aspect_ratio == kScalingSender) { - *out = AspectRatioConstraint::kFixed; - return true; + + ErrorOr<AspectRatioConstraint> constraint = + GetEnum(kAspectRatioConstraintNames, aspect_ratio); + if (constraint.is_error()) { + return false; } - return false; + *out = constraint.value(); + return true; } template <typename T> @@ -160,7 +154,7 @@ bool ParseOptional(const Json::Value& value, absl::optional<T>* out) { return true; } T tentative_out; - if (!T::ParseAndValidate(value, &tentative_out)) { + if (!T::TryParse(value, &tentative_out)) { return false; } *out = tentative_out; @@ -170,9 +164,9 @@ bool ParseOptional(const Json::Value& value, absl::optional<T>* out) { } // namespace // static -bool AspectRatio::ParseAndValidate(const Json::Value& value, AspectRatio* out) { +bool AspectRatio::TryParse(const Json::Value& value, AspectRatio* out) { std::string parsed_value; - if (!json::ParseAndValidateString(value, &parsed_value)) { + if (!json::TryParseString(value, &parsed_value)) { return false; } @@ -194,21 +188,20 @@ bool AspectRatio::IsValid() const { } // static -bool AudioConstraints::ParseAndValidate(const Json::Value& root, - AudioConstraints* out) { - if (!json::ParseAndValidateInt(root[kMaxSampleRate], - &(out->max_sample_rate)) || - !json::ParseAndValidateInt(root[kMaxChannels], &(out->max_channels)) || - !json::ParseAndValidateInt(root[kMaxBitRate], &(out->max_bit_rate))) { +bool AudioConstraints::TryParse(const Json::Value& root, + AudioConstraints* out) { + if (!json::TryParseInt(root[kMaxSampleRate], &(out->max_sample_rate)) || + !json::TryParseInt(root[kMaxChannels], &(out->max_channels)) || + !json::TryParseInt(root[kMaxBitRate], &(out->max_bit_rate))) { return false; } std::chrono::milliseconds max_delay; - if (json::ParseAndValidateMilliseconds(root[kMaxDelay], &max_delay)) { + if (json::TryParseMilliseconds(root[kMaxDelay], &max_delay)) { out->max_delay = max_delay; } - if (!json::ParseAndValidateInt(root[kMinBitRate], &(out->min_bit_rate))) { + if (!json::TryParseInt(root[kMinBitRate], &(out->min_bit_rate))) { out->min_bit_rate = kDefaultAudioMinBitRate; } return out->IsValid(); @@ -233,28 +226,26 @@ bool AudioConstraints::IsValid() const { } // static -bool VideoConstraints::ParseAndValidate(const Json::Value& root, - VideoConstraints* out) { - if (!Dimensions::ParseAndValidate(root[kMaxDimensions], - &(out->max_dimensions)) || - !json::ParseAndValidateInt(root[kMaxBitRate], &(out->max_bit_rate)) || +bool VideoConstraints::TryParse(const Json::Value& root, + VideoConstraints* out) { + if (!Dimensions::TryParse(root[kMaxDimensions], &(out->max_dimensions)) || + !json::TryParseInt(root[kMaxBitRate], &(out->max_bit_rate)) || !ParseOptional<Dimensions>(root[kMinResolution], &(out->min_resolution))) { return false; } std::chrono::milliseconds max_delay; - if (json::ParseAndValidateMilliseconds(root[kMaxDelay], &max_delay)) { + if (json::TryParseMilliseconds(root[kMaxDelay], &max_delay)) { out->max_delay = max_delay; } double max_pixels_per_second; - if (json::ParseAndValidateDouble(root[kMaxPixelsPerSecond], - &max_pixels_per_second)) { + if (json::TryParseDouble(root[kMaxPixelsPerSecond], &max_pixels_per_second)) { out->max_pixels_per_second = max_pixels_per_second; } - if (!json::ParseAndValidateInt(root[kMinBitRate], &(out->min_bit_rate))) { + if (!json::TryParseInt(root[kMinBitRate], &(out->min_bit_rate))) { out->min_bit_rate = kDefaultVideoMinBitRate; } return out->IsValid(); @@ -290,9 +281,9 @@ Json::Value VideoConstraints::ToJson() const { } // static -bool Constraints::ParseAndValidate(const Json::Value& root, Constraints* out) { - if (!AudioConstraints::ParseAndValidate(root[kAudio], &(out->audio)) || - !VideoConstraints::ParseAndValidate(root[kVideo], &(out->video))) { +bool Constraints::TryParse(const Json::Value& root, Constraints* out) { + if (!AudioConstraints::TryParse(root[kAudio], &(out->audio)) || + !VideoConstraints::TryParse(root[kVideo], &(out->video))) { return false; } return out->IsValid(); @@ -311,15 +302,15 @@ Json::Value Constraints::ToJson() const { } // static -bool DisplayDescription::ParseAndValidate(const Json::Value& root, - DisplayDescription* out) { +bool DisplayDescription::TryParse(const Json::Value& root, + DisplayDescription* out) { if (!ParseOptional<Dimensions>(root[kDimensions], &(out->dimensions)) || !ParseOptional<AspectRatio>(root[kAspectRatio], &(out->aspect_ratio))) { return false; } AspectRatioConstraint constraint; - if (AspectRatioConstraintParseAndValidate(root[kScaling], &constraint)) { + if (TryParseAspectRatioConstraint(root[kScaling], &constraint)) { out->aspect_ratio_constraint = absl::optional<AspectRatioConstraint>(std::move(constraint)); } else { @@ -368,11 +359,14 @@ Json::Value DisplayDescription::ToJson() const { return root; } -bool Answer::ParseAndValidate(const Json::Value& root, Answer* out) { - if (!json::ParseAndValidateInt(root[kUdpPort], &(out->udp_port)) || - !json::ParseAndValidateIntArray(root[kSendIndexes], - &(out->send_indexes)) || - !json::ParseAndValidateUintArray(root[kSsrcs], &(out->ssrcs)) || +bool Answer::ParseAndValidate(const Json::Value& value, Answer* out) { + return TryParse(value, out); +} + +bool Answer::TryParse(const Json::Value& root, Answer* out) { + if (!json::TryParseInt(root[kUdpPort], &(out->udp_port)) || + !json::TryParseIntArray(root[kSendIndexes], &(out->send_indexes)) || + !json::TryParseUintArray(root[kSsrcs], &(out->ssrcs)) || !ParseOptional<Constraints>(root[kConstraints], &(out->constraints)) || !ParseOptional<DisplayDescription>(root[kDisplay], &(out->display))) { return false; @@ -380,12 +374,10 @@ bool Answer::ParseAndValidate(const Json::Value& root, Answer* out) { // These function set to empty array if not present, so we can ignore // the return value for optional values. - json::ParseAndValidateIntArray(root[kReceiverRtcpEventLog], - &(out->receiver_rtcp_event_log)); - json::ParseAndValidateIntArray(root[kReceiverRtcpDscp], - &(out->receiver_rtcp_dscp)); - json::ParseAndValidateStringArray(root[kRtpExtensions], - &(out->rtp_extensions)); + json::TryParseIntArray(root[kReceiverRtcpEventLog], + &(out->receiver_rtcp_event_log)); + json::TryParseIntArray(root[kReceiverRtcpDscp], &(out->receiver_rtcp_dscp)); + json::TryParseStringArray(root[kRtpExtensions], &(out->rtp_extensions)); return out->IsValid(); } diff --git a/cast/streaming/answer_messages.h b/cast/streaming/answer_messages.h index 3a811073..7095e455 100644 --- a/cast/streaming/answer_messages.h +++ b/cast/streaming/answer_messages.h @@ -29,14 +29,14 @@ namespace cast { // readability of the structs provided in this file by cutting down on the // amount of obscuring boilerplate code. For each of the following struct // definitions, the following method definitions are shared: -// (1) ParseAndValidate. Shall return a boolean indicating whether the out +// (1) TryParse. Shall return a boolean indicating whether the out // parameter is in a valid state after checking bounds and restrictions. // (2) ToJson. Should return a proper JSON object. Assumes that IsValid() // has been called already, OSP_DCHECKs if not IsValid(). -// (3) IsValid. Used by both ParseAndValidate and ToJson to ensure that the +// (3) IsValid. Used by both TryParse and ToJson to ensure that the // object is in a good state. struct AudioConstraints { - static bool ParseAndValidate(const Json::Value& value, AudioConstraints* out); + static bool TryParse(const Json::Value& value, AudioConstraints* out); Json::Value ToJson() const; bool IsValid() const; @@ -48,7 +48,7 @@ struct AudioConstraints { }; struct VideoConstraints { - static bool ParseAndValidate(const Json::Value& value, VideoConstraints* out); + static bool TryParse(const Json::Value& value, VideoConstraints* out); Json::Value ToJson() const; bool IsValid() const; @@ -61,7 +61,7 @@ struct VideoConstraints { }; struct Constraints { - static bool ParseAndValidate(const Json::Value& value, Constraints* out); + static bool TryParse(const Json::Value& value, Constraints* out); Json::Value ToJson() const; bool IsValid() const; @@ -75,7 +75,7 @@ struct Constraints { enum class AspectRatioConstraint : uint8_t { kVariable = 0, kFixed }; struct AspectRatio { - static bool ParseAndValidate(const Json::Value& value, AspectRatio* out); + static bool TryParse(const Json::Value& value, AspectRatio* out); bool IsValid() const; bool operator==(const AspectRatio& other) const { @@ -87,8 +87,7 @@ struct AspectRatio { }; struct DisplayDescription { - static bool ParseAndValidate(const Json::Value& value, - DisplayDescription* out); + static bool TryParse(const Json::Value& value, DisplayDescription* out); Json::Value ToJson() const; bool IsValid() const; @@ -100,7 +99,10 @@ struct DisplayDescription { }; struct Answer { + // TODO(jophba): DEPRECATED, remove separately. static bool ParseAndValidate(const Json::Value& value, Answer* out); + + static bool TryParse(const Json::Value& value, Answer* out); Json::Value ToJson() const; bool IsValid() const; diff --git a/cast/streaming/answer_messages_unittest.cc b/cast/streaming/answer_messages_unittest.cc index eb354ba4..3d618828 100644 --- a/cast/streaming/answer_messages_unittest.cc +++ b/cast/streaming/answer_messages_unittest.cc @@ -156,7 +156,7 @@ void ExpectFailureOnParse(absl::string_view raw_json) { ASSERT_TRUE(root.is_value()); Answer answer; - EXPECT_FALSE(Answer::ParseAndValidate(std::move(root.value()), &answer)); + EXPECT_FALSE(Answer::TryParse(std::move(root.value()), &answer)); EXPECT_FALSE(answer.IsValid()); } @@ -168,7 +168,7 @@ void ExpectSuccessOnParse(absl::string_view raw_json, Answer* out = nullptr) { ASSERT_TRUE(root.is_value()); Answer answer; - ASSERT_TRUE(Answer::ParseAndValidate(std::move(root.value()), &answer)); + ASSERT_TRUE(Answer::TryParse(std::move(root.value()), &answer)); EXPECT_TRUE(answer.IsValid()); if (out) { *out = std::move(answer); @@ -495,7 +495,7 @@ TEST(AnswerMessagesTest, AspectRatioIsValid) { EXPECT_FALSE(kInvalidHeight.IsValid()); } -TEST(AnswerMessagesTest, AspectRatioParseAndValidate) { +TEST(AnswerMessagesTest, AspectRatioTryParse) { const Json::Value kValid = "16:9"; const Json::Value kWrongDelimiter = "16-9"; const Json::Value kTooManyFields = "16:9:3"; @@ -510,24 +510,24 @@ TEST(AnswerMessagesTest, AspectRatioParseAndValidate) { const Json::Value kZeroHeight = "16:0"; AspectRatio out; - EXPECT_TRUE(AspectRatio::ParseAndValidate(kValid, &out)); + EXPECT_TRUE(AspectRatio::TryParse(kValid, &out)); EXPECT_EQ(out.width, 16); EXPECT_EQ(out.height, 9); - EXPECT_FALSE(AspectRatio::ParseAndValidate(kWrongDelimiter, &out)); - EXPECT_FALSE(AspectRatio::ParseAndValidate(kTooManyFields, &out)); - EXPECT_FALSE(AspectRatio::ParseAndValidate(kTooFewFields, &out)); - EXPECT_FALSE(AspectRatio::ParseAndValidate(kWrongDelimiter, &out)); - EXPECT_FALSE(AspectRatio::ParseAndValidate(kNoDelimiter, &out)); - EXPECT_FALSE(AspectRatio::ParseAndValidate(kNegativeWidth, &out)); - EXPECT_FALSE(AspectRatio::ParseAndValidate(kNegativeHeight, &out)); - EXPECT_FALSE(AspectRatio::ParseAndValidate(kNegativeBoth, &out)); - EXPECT_FALSE(AspectRatio::ParseAndValidate(kNonNumberWidth, &out)); - EXPECT_FALSE(AspectRatio::ParseAndValidate(kNonNumberHeight, &out)); - EXPECT_FALSE(AspectRatio::ParseAndValidate(kZeroWidth, &out)); - EXPECT_FALSE(AspectRatio::ParseAndValidate(kZeroHeight, &out)); + EXPECT_FALSE(AspectRatio::TryParse(kWrongDelimiter, &out)); + EXPECT_FALSE(AspectRatio::TryParse(kTooManyFields, &out)); + EXPECT_FALSE(AspectRatio::TryParse(kTooFewFields, &out)); + EXPECT_FALSE(AspectRatio::TryParse(kWrongDelimiter, &out)); + EXPECT_FALSE(AspectRatio::TryParse(kNoDelimiter, &out)); + EXPECT_FALSE(AspectRatio::TryParse(kNegativeWidth, &out)); + EXPECT_FALSE(AspectRatio::TryParse(kNegativeHeight, &out)); + EXPECT_FALSE(AspectRatio::TryParse(kNegativeBoth, &out)); + EXPECT_FALSE(AspectRatio::TryParse(kNonNumberWidth, &out)); + EXPECT_FALSE(AspectRatio::TryParse(kNonNumberHeight, &out)); + EXPECT_FALSE(AspectRatio::TryParse(kZeroWidth, &out)); + EXPECT_FALSE(AspectRatio::TryParse(kZeroHeight, &out)); } -TEST(AnswerMessagesTest, DisplayDescriptionParseAndValidate) { +TEST(AnswerMessagesTest, DisplayDescriptionTryParse) { Json::Value valid_scaling; valid_scaling["scaling"] = "receiver"; Json::Value invalid_scaling; @@ -553,25 +553,23 @@ TEST(AnswerMessagesTest, DisplayDescriptionParseAndValidate) { aspect_ratio_and_constraint["aspectRatio"] = "4:3"; DisplayDescription out; - ASSERT_TRUE(DisplayDescription::ParseAndValidate(valid_scaling, &out)); + ASSERT_TRUE(DisplayDescription::TryParse(valid_scaling, &out)); ASSERT_TRUE(out.aspect_ratio_constraint.has_value()); EXPECT_EQ(out.aspect_ratio_constraint.value(), AspectRatioConstraint::kVariable); - EXPECT_FALSE(DisplayDescription::ParseAndValidate(invalid_scaling, &out)); - EXPECT_TRUE( - DisplayDescription::ParseAndValidate(invalid_scaling_valid_ratio, &out)); + EXPECT_FALSE(DisplayDescription::TryParse(invalid_scaling, &out)); + EXPECT_TRUE(DisplayDescription::TryParse(invalid_scaling_valid_ratio, &out)); - ASSERT_TRUE(DisplayDescription::ParseAndValidate(valid_dimensions, &out)); + ASSERT_TRUE(DisplayDescription::TryParse(valid_dimensions, &out)); ASSERT_TRUE(out.dimensions.has_value()); EXPECT_EQ(1920, out.dimensions->width); EXPECT_EQ(1080, out.dimensions->height); EXPECT_EQ((SimpleFraction{30, 1}), out.dimensions->frame_rate); - EXPECT_FALSE(DisplayDescription::ParseAndValidate(invalid_dimensions, &out)); + EXPECT_FALSE(DisplayDescription::TryParse(invalid_dimensions, &out)); - ASSERT_TRUE( - DisplayDescription::ParseAndValidate(aspect_ratio_and_constraint, &out)); + ASSERT_TRUE(DisplayDescription::TryParse(aspect_ratio_and_constraint, &out)); EXPECT_EQ(AspectRatioConstraint::kFixed, out.aspect_ratio_constraint.value()); } diff --git a/cast/streaming/offer_messages.cc b/cast/streaming/offer_messages.cc index f21decb1..f438d9be 100644 --- a/cast/streaming/offer_messages.cc +++ b/cast/streaming/offer_messages.cc @@ -33,36 +33,39 @@ constexpr char kAudioSourceType[] = "audio_source"; constexpr char kVideoSourceType[] = "video_source"; constexpr char kStreamType[] = "type"; -ErrorOr<RtpPayloadType> ParseRtpPayloadType(const Json::Value& parent, - const std::string& field) { - auto t = json::ParseInt(parent, field); - if (!t) { - return t.error(); +EnumNameTable<CastMode, 2> kCastModeNames{ + {{"mirroring", CastMode::kMirroring}, {"remoting", CastMode::kRemoting}}}; + +bool TryParseRtpPayloadType(const Json::Value& value, RtpPayloadType* out) { + int t; + if (!json::TryParseInt(value, &t)) { + return false; } - uint8_t t_small = t.value(); - if (t_small != t.value() || !IsRtpPayloadType(t_small)) { - return Error(Error::Code::kParameterInvalid, - "Received invalid RTP Payload Type."); + uint8_t t_small = t; + if (t_small != t || !IsRtpPayloadType(t_small)) { + return false; } - return static_cast<RtpPayloadType>(t_small); + *out = static_cast<RtpPayloadType>(t_small); + return true; } -ErrorOr<int> ParseRtpTimebase(const Json::Value& parent, - const std::string& field) { - auto error_or_raw = json::ParseString(parent, field); - if (!error_or_raw) { - return error_or_raw.error(); +bool TryParseRtpTimebase(const Json::Value& value, int* out) { + std::string raw_timebase; + if (!json::TryParseString(value, &raw_timebase)) { + return false; } // The spec demands a leading 1, so this isn't really a fraction. - const auto fraction = SimpleFraction::FromString(error_or_raw.value()); + const auto fraction = SimpleFraction::FromString(raw_timebase); if (fraction.is_error() || !fraction.value().is_positive() || fraction.value().numerator() != 1) { - return json::CreateParseError("RTP timebase"); + return false; } - return fraction.value().denominator(); + + *out = fraction.value().denominator(); + return true; } // For a hex byte, the conversion is 4 bits to 1 character, e.g. @@ -70,203 +73,119 @@ ErrorOr<int> ParseRtpTimebase(const Json::Value& parent, constexpr int kHexDigitsPerByte = 2; constexpr int kAesBytesSize = 16; constexpr int kAesStringLength = kAesBytesSize * kHexDigitsPerByte; -ErrorOr<std::array<uint8_t, kAesBytesSize>> ParseAesHexBytes( - const Json::Value& parent, - const std::string& field) { - auto hex_string = json::ParseString(parent, field); - if (!hex_string) { - return hex_string.error(); +bool TryParseAesHexBytes(const Json::Value& value, + std::array<uint8_t, kAesBytesSize>* out) { + std::string hex_string; + if (!json::TryParseString(value, &hex_string)) { + return false; } constexpr int kHexDigitsPerScanField = 16; constexpr int kNumScanFields = kAesStringLength / kHexDigitsPerScanField; uint64_t quads[kNumScanFields]; int chars_scanned; - if (hex_string.value().size() == kAesStringLength && - sscanf(hex_string.value().c_str(), "%16" SCNx64 "%16" SCNx64 "%n", - &quads[0], &quads[1], &chars_scanned) == kNumScanFields && + if (hex_string.size() == kAesStringLength && + sscanf(hex_string.c_str(), "%16" SCNx64 "%16" SCNx64 "%n", &quads[0], + &quads[1], &chars_scanned) == kNumScanFields && chars_scanned == kAesStringLength && - std::none_of(hex_string.value().begin(), hex_string.value().end(), + std::none_of(hex_string.begin(), hex_string.end(), [](char c) { return std::isspace(c); })) { - std::array<uint8_t, kAesBytesSize> bytes; - WriteBigEndian(quads[0], bytes.data()); - WriteBigEndian(quads[1], bytes.data() + 8); - return bytes; + WriteBigEndian(quads[0], out->data()); + WriteBigEndian(quads[1], out->data() + 8); + return true; } - return json::CreateParseError("AES hex string bytes"); -} -ErrorOr<Stream> ParseStream(const Json::Value& value, Stream::Type type) { - auto index = json::ParseInt(value, "index"); - if (!index) { - return index.error(); - } - // If channel is omitted, the default value is used later. - auto channels = json::ParseInt(value, "channels"); - if (channels.is_value() && channels.value() <= 0) { - return json::CreateParameterError("channel"); - } - auto rtp_profile = json::ParseString(value, "rtpProfile"); - if (!rtp_profile) { - return rtp_profile.error(); - } - auto rtp_payload_type = ParseRtpPayloadType(value, "rtpPayloadType"); - if (!rtp_payload_type) { - return rtp_payload_type.error(); - } - auto ssrc = json::ParseUint(value, "ssrc"); - if (!ssrc) { - return ssrc.error(); - } - auto aes_key = ParseAesHexBytes(value, "aesKey"); - auto aes_iv_mask = ParseAesHexBytes(value, "aesIvMask"); - if (!aes_key || !aes_iv_mask) { - return Error(Error::Code::kUnencryptedOffer, - "Offer stream must have both a valid aesKey and aesIvMask"); - } - auto rtp_timebase = ParseRtpTimebase(value, "timeBase"); - if (!rtp_timebase) { - return rtp_timebase.error(); - } - if (rtp_timebase.value() < - std::min(kDefaultAudioMinSampleRate, kRtpVideoTimebase) || - rtp_timebase.value() > kRtpVideoTimebase) { - return json::CreateParameterError("rtp_timebase (sample rate)"); - } + return false; +} - auto target_delay = json::ParseInt(value, "targetDelay"); - std::chrono::milliseconds target_delay_ms = kDefaultTargetPlayoutDelay; - if (target_delay) { - auto d = std::chrono::milliseconds(target_delay.value()); - if (kMinTargetPlayoutDelay <= d && d <= kMaxTargetPlayoutDelay) { - target_delay_ms = d; +absl::string_view ToString(Stream::Type type) { + switch (type) { + case Stream::Type::kAudioSource: + return kAudioSourceType; + case Stream::Type::kVideoSource: + return kVideoSourceType; + default: { + OSP_NOTREACHED(); } } - - auto receiver_rtcp_event_log = json::ParseBool(value, "receiverRtcpEventLog"); - auto receiver_rtcp_dscp = json::ParseString(value, "receiverRtcpDscp"); - return Stream{index.value(), - type, - channels.value(type == Stream::Type::kAudioSource - ? kDefaultNumAudioChannels - : kDefaultNumVideoChannels), - rtp_payload_type.value(), - ssrc.value(), - target_delay_ms, - aes_key.value(), - aes_iv_mask.value(), - receiver_rtcp_event_log.value({}), - receiver_rtcp_dscp.value({}), - rtp_timebase.value()}; } -ErrorOr<AudioStream> ParseAudioStream(const Json::Value& value) { - auto stream = ParseStream(value, Stream::Type::kAudioSource); - if (!stream) { - return stream.error(); - } - auto bit_rate = json::ParseInt(value, "bitRate"); - if (!bit_rate) { - return bit_rate.error(); - } - - auto codec_name = json::ParseString(value, kCodecName); - if (!codec_name) { - return codec_name.error(); - } - ErrorOr<AudioCodec> codec = StringToAudioCodec(codec_name.value()); - if (!codec) { - return Error(Error::Code::kUnknownCodec, - "Codec is not known, can't use stream"); - } - - // A bit rate of 0 is valid for some codec types, so we don't enforce here. - if (bit_rate.value() < 0) { - return json::CreateParameterError("bit rate"); - } - return AudioStream{stream.value(), codec.value(), bit_rate.value()}; -} +bool TryParseResolutions(const Json::Value& value, + std::vector<Resolution>* out) { + out->clear(); -ErrorOr<std::vector<Resolution>> ParseResolutions(const Json::Value& parent, - const std::string& field) { - std::vector<Resolution> resolutions; // Some legacy senders don't provide resolutions, so just return empty. - const Json::Value& value = parent[field]; if (!value.isArray() || value.empty()) { - return resolutions; + return false; } for (Json::ArrayIndex i = 0; i < value.size(); ++i) { Resolution resolution; - if (!Resolution::ParseAndValidate(value[i], &resolution)) { - return Error(Error::Code::kJsonParseError); + if (!Resolution::TryParse(value[i], &resolution)) { + out->clear(); + return false; } - resolutions.push_back(std::move(resolution)); + out->push_back(std::move(resolution)); } - return resolutions; + return true; } -ErrorOr<VideoStream> ParseVideoStream(const Json::Value& value) { - auto stream = ParseStream(value, Stream::Type::kVideoSource); - if (!stream) { - return stream.error(); +} // namespace + +Error Stream::TryParse(const Json::Value& value, + Stream::Type type, + Stream* out) { + out->type = type; + + if (!json::TryParseInt(value["index"], &out->index) || + !json::TryParseUint(value["ssrc"], &out->ssrc) || + !TryParseRtpPayloadType(value["rtpPayloadType"], + &out->rtp_payload_type) || + !TryParseRtpTimebase(value["timeBase"], &out->rtp_timebase)) { + return Error(Error::Code::kJsonParseError, + "Offer stream has missing or invalid mandatory field"); } - auto codec_name = json::ParseString(value, kCodecName); - if (!codec_name) { - return codec_name.error(); + + if (!json::TryParseInt(value["channels"], &out->channels)) { + out->channels = out->type == Stream::Type::kAudioSource + ? kDefaultNumAudioChannels + : kDefaultNumVideoChannels; + } else if (out->channels <= 0) { + return Error(Error::Code::kJsonParseError, "Invalid channel count"); } - ErrorOr<VideoCodec> codec = StringToVideoCodec(codec_name.value()); - if (!codec) { - return Error(Error::Code::kUnknownCodec, - "Codec is not known, can't use stream"); + + if (!TryParseAesHexBytes(value["aesKey"], &out->aes_key) || + !TryParseAesHexBytes(value["aesIvMask"], &out->aes_iv_mask)) { + return Error(Error::Code::kUnencryptedOffer, + "Offer stream must have both a valid aesKey and aesIvMask"); } - auto resolutions = ParseResolutions(value, "resolutions"); - if (!resolutions) { - return resolutions.error(); + if (out->rtp_timebase < + std::min(kDefaultAudioMinSampleRate, kRtpVideoTimebase) || + out->rtp_timebase > kRtpVideoTimebase) { + return Error(Error::Code::kJsonParseError, "rtp_timebase (sample rate)"); } - auto raw_max_frame_rate = json::ParseString(value, "maxFrameRate"); - SimpleFraction max_frame_rate{kDefaultMaxFrameRate, 1}; - if (raw_max_frame_rate.is_value()) { - auto parsed = SimpleFraction::FromString(raw_max_frame_rate.value()); - if (parsed.is_value() && parsed.value().is_positive()) { - max_frame_rate = parsed.value(); + out->target_delay = kDefaultTargetPlayoutDelay; + int target_delay; + if (json::TryParseInt(value["targetDelay"], &target_delay)) { + auto d = std::chrono::milliseconds(target_delay); + if (kMinTargetPlayoutDelay <= d && d <= kMaxTargetPlayoutDelay) { + out->target_delay = d; } } - auto profile = json::ParseString(value, "profile"); - auto protection = json::ParseString(value, "protection"); - auto max_bit_rate = json::ParseInt(value, "maxBitRate"); - auto level = json::ParseString(value, "level"); - auto error_recovery_mode = json::ParseString(value, "errorRecoveryMode"); - return VideoStream{stream.value(), - codec.value(), - max_frame_rate, - max_bit_rate.value(4 << 20), - protection.value({}), - profile.value({}), - level.value({}), - resolutions.value(), - error_recovery_mode.value({})}; -} - -absl::string_view ToString(Stream::Type type) { - switch (type) { - case Stream::Type::kAudioSource: - return kAudioSourceType; - case Stream::Type::kVideoSource: - return kVideoSourceType; - default: { - OSP_NOTREACHED(); - } + if (!json::TryParseBool(value["receiverRtcpEventLog"], + &out->receiver_rtcp_event_log)) { + out->receiver_rtcp_event_log = false; + } + if (!json::TryParseString(value["receiverRtcpDscp"], + &out->receiver_rtcp_dscp)) { + out->receiver_rtcp_dscp = {}; } -} - -EnumNameTable<CastMode, 2> kCastModeNames{ - {{"mirroring", CastMode::kMirroring}, {"remoting", CastMode::kRemoting}}}; -} // namespace + return Error::None(); +} Json::Value Stream::ToJson() const { OSP_DCHECK(IsValid()); @@ -297,6 +216,28 @@ bool Stream::IsValid() const { rtp_timebase >= 1; } +Error AudioStream::TryParse(const Json::Value& value, AudioStream* out) { + Error error = + Stream::TryParse(value, Stream::Type::kAudioSource, &out->stream); + if (!error.ok()) { + return error; + } + + std::string codec_name; + if (!json::TryParseInt(value["bitRate"], &out->bit_rate) || + out->bit_rate < 0 || + !json::TryParseString(value[kCodecName], &codec_name)) { + return Error(Error::Code::kJsonParseError, "Invalid audio stream field"); + } + ErrorOr<AudioCodec> codec = StringToAudioCodec(codec_name); + if (!codec) { + return Error(Error::Code::kUnknownCodec, + "Codec is not known, can't use stream"); + } + out->codec = codec.value(); + return Error::None(); +} + Json::Value AudioStream::ToJson() const { OSP_DCHECK(IsValid()); @@ -310,6 +251,45 @@ bool AudioStream::IsValid() const { return bit_rate >= 0 && stream.IsValid(); } +Error VideoStream::TryParse(const Json::Value& value, VideoStream* out) { + Error error = + Stream::TryParse(value, Stream::Type::kVideoSource, &out->stream); + if (!error.ok()) { + return error; + } + + std::string codec_name; + if (!json::TryParseString(value[kCodecName], &codec_name)) { + return Error(Error::Code::kJsonParseError, "Video stream missing codec"); + } + ErrorOr<VideoCodec> codec = StringToVideoCodec(codec_name); + if (!codec) { + return Error(Error::Code::kUnknownCodec, + "Codec is not known, can't use stream"); + } + out->codec = codec.value(); + + out->max_frame_rate = SimpleFraction{kDefaultMaxFrameRate, 1}; + std::string raw_max_frame_rate; + if (json::TryParseString(value["maxFrameRate"], &raw_max_frame_rate)) { + auto parsed = SimpleFraction::FromString(raw_max_frame_rate); + if (parsed.is_value() && parsed.value().is_positive()) { + out->max_frame_rate = parsed.value(); + } + } + + TryParseResolutions(value["resolutions"], &out->resolutions); + json::TryParseString(value["profile"], &out->profile); + json::TryParseString(value["protection"], &out->protection); + json::TryParseString(value["level"], &out->level); + json::TryParseString(value["errorRecoveryMode"], &out->error_recovery_mode); + if (!json::TryParseInt(value["maxBitRate"], &out->max_bit_rate)) { + out->max_bit_rate = 4 << 20; + } + + return Error::None(); +} + Json::Value VideoStream::ToJson() const { OSP_DCHECK(IsValid()); @@ -335,55 +315,54 @@ bool VideoStream::IsValid() const { } // static -ErrorOr<Offer> Offer::Parse(const Json::Value& root) { +Error Offer::TryParse(const Json::Value& root, Offer* out) { if (!root.isObject()) { - return json::CreateParseError("null offer"); + return Error(Error::Code::kJsonParseError, "null offer"); } const ErrorOr<CastMode> cast_mode = GetEnum(kCastModeNames, root["castMode"].asString()); Json::Value supported_streams = root[kSupportedStreams]; if (!supported_streams.isArray()) { - return json::CreateParseError("supported streams in offer"); + return Error(Error::Code::kJsonParseError, "supported streams in offer"); } std::vector<AudioStream> audio_streams; std::vector<VideoStream> video_streams; for (Json::ArrayIndex i = 0; i < supported_streams.size(); ++i) { const Json::Value& fields = supported_streams[i]; - auto type = json::ParseString(fields, kStreamType); - if (!type) { - return type.error(); + std::string type; + if (!json::TryParseString(fields[kStreamType], &type)) { + return Error(Error::Code::kJsonParseError, "Missing stream type"); } - if (type.value() == kAudioSourceType) { - auto stream = ParseAudioStream(fields); - if (!stream) { - if (stream.error().code() == Error::Code::kUnknownCodec) { - OSP_DVLOG << "Dropping audio stream due to unknown codec: " - << stream.error(); - continue; - } else { - return stream.error(); - } + Error error; + if (type == kAudioSourceType) { + AudioStream stream; + error = AudioStream::TryParse(fields, &stream); + if (error.ok()) { + audio_streams.push_back(std::move(stream)); } - audio_streams.push_back(std::move(stream.value())); - } else if (type.value() == kVideoSourceType) { - auto stream = ParseVideoStream(fields); - if (!stream) { - if (stream.error().code() == Error::Code::kUnknownCodec) { - OSP_DVLOG << "Dropping video stream due to unknown codec: " - << stream.error(); - continue; - } else { - return stream.error(); - } + } else if (type == kVideoSourceType) { + VideoStream stream; + error = VideoStream::TryParse(fields, &stream); + if (error.ok()) { + video_streams.push_back(std::move(stream)); + } + } + + if (!error.ok()) { + if (error.code() == Error::Code::kUnknownCodec) { + OSP_DVLOG << "Dropping audio stream due to unknown codec: " << error; + continue; + } else { + return error; } - video_streams.push_back(std::move(stream.value())); } } - return Offer{cast_mode.value(CastMode::kMirroring), std::move(audio_streams), + *out = Offer{cast_mode.value(CastMode::kMirroring), std::move(audio_streams), std::move(video_streams)}; + return Error::None(); } Json::Value Offer::ToJson() const { diff --git a/cast/streaming/offer_messages.h b/cast/streaming/offer_messages.h index efb3a1f6..80458954 100644 --- a/cast/streaming/offer_messages.h +++ b/cast/streaming/offer_messages.h @@ -46,6 +46,9 @@ constexpr int kDefaultNumAudioChannels = 2; struct Stream { enum class Type : uint8_t { kAudioSource, kVideoSource }; + static Error TryParse(const Json::Value& root, + Stream::Type type, + Stream* out); Json::Value ToJson() const; bool IsValid() const; @@ -68,6 +71,7 @@ struct Stream { }; struct AudioStream { + static Error TryParse(const Json::Value& root, AudioStream* out); Json::Value ToJson() const; bool IsValid() const; @@ -78,6 +82,7 @@ struct AudioStream { struct VideoStream { + static Error TryParse(const Json::Value& root, VideoStream* out); Json::Value ToJson() const; bool IsValid() const; @@ -95,7 +100,7 @@ struct VideoStream { enum class CastMode : uint8_t { kMirroring, kRemoting }; struct Offer { - static ErrorOr<Offer> Parse(const Json::Value& root); + static Error TryParse(const Json::Value& root, Offer* out); Json::Value ToJson() const; bool IsValid() const; diff --git a/cast/streaming/offer_messages_unittest.cc b/cast/streaming/offer_messages_unittest.cc index e1e70550..038d2f80 100644 --- a/cast/streaming/offer_messages_unittest.cc +++ b/cast/streaming/offer_messages_unittest.cc @@ -90,10 +90,12 @@ void ExpectFailureOnParse( absl::optional<Error::Code> expected = absl::nullopt) { ErrorOr<Json::Value> root = json::Parse(body); ASSERT_TRUE(root.is_value()) << root.error(); - ErrorOr<Offer> error_or_offer = Offer::Parse(std::move(root.value())); - EXPECT_TRUE(error_or_offer.is_error()); + + Offer offer; + Error error = Offer::TryParse(std::move(root.value()), &offer); + EXPECT_FALSE(error.ok()); if (expected) { - EXPECT_EQ(expected, error_or_offer.error().code()); + EXPECT_EQ(expected, error.code()); } } @@ -200,7 +202,9 @@ TEST(OfferTest, CanParseValidButStreamlessOffer) { "supportedStreams": [] })"); ASSERT_TRUE(root.is_value()) << root.error(); - EXPECT_TRUE(Offer::Parse(std::move(root.value())).is_value()); + + Offer offer; + EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &offer).ok()); } TEST(OfferTest, ErrorOnMissingAudioStreamMandatoryField) { @@ -249,7 +253,8 @@ TEST(OfferTest, CanParseValidButMinimalAudioOffer) { }] })"); ASSERT_TRUE(root.is_value()); - EXPECT_TRUE(Offer::Parse(std::move(root.value())).is_value()); + Offer offer; + EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &offer).ok()); } TEST(OfferTest, CanParseValidZeroBitRateAudioOffer) { @@ -270,8 +275,8 @@ TEST(OfferTest, CanParseValidZeroBitRateAudioOffer) { }] })"); ASSERT_TRUE(root.is_value()) << root.error(); - const auto offer = Offer::Parse(std::move(root.value())); - EXPECT_TRUE(offer.is_value()) << offer.error(); + Offer offer; + EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &offer).ok()); } TEST(OfferTest, ErrorOnInvalidRtpTimebase) { @@ -439,26 +444,29 @@ TEST(OfferTest, CanParseValidButMinimalVideoOffer) { })"); ASSERT_TRUE(root.is_value()); - EXPECT_TRUE(Offer::Parse(std::move(root.value())).is_value()); + Offer offer; + EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &offer).ok()); } TEST(OfferTest, CanParseValidOffer) { ErrorOr<Json::Value> root = json::Parse(kValidOffer); ASSERT_TRUE(root.is_value()); - ErrorOr<Offer> offer = Offer::Parse(std::move(root.value())); + Offer offer; + EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &offer).ok()); - ExpectEqualsValidOffer(offer.value()); + ExpectEqualsValidOffer(offer); } TEST(OfferTest, ParseAndToJsonResultsInSameOffer) { ErrorOr<Json::Value> root = json::Parse(kValidOffer); ASSERT_TRUE(root.is_value()); - ErrorOr<Offer> offer = Offer::Parse(std::move(root.value())); - ExpectEqualsValidOffer(offer.value()); + Offer offer; + EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &offer).ok()); + ExpectEqualsValidOffer(offer); - ErrorOr<Offer> reparsed_offer = - Offer::Parse(std::move(offer.value().ToJson())); - ExpectEqualsValidOffer(reparsed_offer.value()); + Offer reparsed_offer; + EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &reparsed_offer).ok()); + ExpectEqualsValidOffer(reparsed_offer); } // We don't want to enforce that a given offer must have both audio and @@ -466,9 +474,10 @@ TEST(OfferTest, ParseAndToJsonResultsInSameOffer) { TEST(OfferTest, IsValidWithMissingStreams) { ErrorOr<Json::Value> root = json::Parse(kValidOffer); ASSERT_TRUE(root.is_value()); - ErrorOr<Offer> offer = Offer::Parse(std::move(root.value())); - ExpectEqualsValidOffer(offer.value()); - const Offer valid_offer = std::move(offer.value()); + Offer offer; + EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &offer).ok()); + ExpectEqualsValidOffer(offer); + const Offer valid_offer = std::move(offer); Offer missing_audio_streams = valid_offer; missing_audio_streams.audio_streams.clear(); @@ -482,15 +491,15 @@ TEST(OfferTest, IsValidWithMissingStreams) { TEST(OfferTest, InvalidIfInvalidStreams) { ErrorOr<Json::Value> root = json::Parse(kValidOffer); ASSERT_TRUE(root.is_value()); - ErrorOr<Offer> offer = Offer::Parse(std::move(root.value())); - ExpectEqualsValidOffer(offer.value()); - const Offer valid_offer = std::move(offer.value()); + Offer offer; + EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &offer).ok()); + ExpectEqualsValidOffer(offer); - Offer video_stream_invalid = valid_offer; + Offer video_stream_invalid = offer; video_stream_invalid.video_streams[0].max_frame_rate = SimpleFraction{1, 0}; EXPECT_FALSE(video_stream_invalid.IsValid()); - Offer audio_stream_invalid = valid_offer; + Offer audio_stream_invalid = offer; video_stream_invalid.audio_streams[0].bit_rate = 0; EXPECT_FALSE(video_stream_invalid.IsValid()); } diff --git a/cast/streaming/receiver_message.cc b/cast/streaming/receiver_message.cc index ec8f9619..e1af610c 100644 --- a/cast/streaming/receiver_message.cc +++ b/cast/streaming/receiver_message.cc @@ -41,7 +41,7 @@ EnumNameTable<MediaCapability, 9> kMediaCapabilityNames{{ ReceiverMessage::Type GetMessageType(const Json::Value& root) { std::string type; - if (!json::ParseAndValidateString(root[kMessageType], &type)) { + if (!json::TryParseString(root[kMessageType], &type)) { return ReceiverMessage::Type::kUnknown; } @@ -51,10 +51,9 @@ ReceiverMessage::Type GetMessageType(const Json::Value& root) { return parsed.value(ReceiverMessage::Type::kUnknown); } -bool ParseAndValidateCapability(const Json::Value& value, - MediaCapability* out) { +bool TryParseCapability(const Json::Value& value, MediaCapability* out) { std::string c; - if (!json::ParseAndValidateString(value, &c)) { + if (!json::TryParseString(value, &c)) { return false; } @@ -78,8 +77,8 @@ ErrorOr<ReceiverError> ReceiverError::Parse(const Json::Value& value) { int code; std::string description; - if (!json::ParseAndValidateInt(value[kErrorCode], &code) || - !json::ParseAndValidateString(value[kErrorDescription], &description)) { + if (!json::TryParseInt(value[kErrorCode], &code) || + !json::TryParseString(value[kErrorDescription], &description)) { return Error::Code::kJsonParseError; } return ReceiverError{code, description}; @@ -101,13 +100,13 @@ ErrorOr<ReceiverCapability> ReceiverCapability::Parse( } int remoting_version; - if (!json::ParseAndValidateInt(value["remoting"], &remoting_version)) { + if (!json::TryParseInt(value["remoting"], &remoting_version)) { remoting_version = ReceiverCapability::kRemotingVersionUnknown; } std::vector<MediaCapability> capabilities; - if (!json::ParseAndValidateArray<MediaCapability>( - value["mediaCaps"], ParseAndValidateCapability, &capabilities)) { + if (!json::TryParseArray<MediaCapability>( + value["mediaCaps"], TryParseCapability, &capabilities)) { return Error(Error::Code::kJsonParseError, "Failed to parse media capabilities"); } @@ -129,14 +128,14 @@ Json::Value ReceiverCapability::ToJson() const { // static ErrorOr<ReceiverMessage> ReceiverMessage::Parse(const Json::Value& value) { ReceiverMessage message; - if (!value || !json::ParseAndValidateInt(value[kSequenceNumber], - &(message.sequence_number))) { + if (!value || + !json::TryParseInt(value[kSequenceNumber], &(message.sequence_number))) { return Error(Error::Code::kJsonParseError, "Failed to parse sequence number"); } std::string result; - if (!json::ParseAndValidateString(value[kResult], &result)) { + if (!json::TryParseString(value[kResult], &result)) { result = kResultError; } @@ -155,8 +154,8 @@ ErrorOr<ReceiverMessage> ReceiverMessage::Parse(const Json::Value& value) { switch (message.type) { case Type::kAnswer: { Answer answer; - if (openscreen::cast::Answer::ParseAndValidate(value[kAnswerMessageBody], - &answer)) { + if (openscreen::cast::Answer::TryParse(value[kAnswerMessageBody], + &answer)) { message.body = std::move(answer); message.valid = true; } @@ -174,7 +173,7 @@ ErrorOr<ReceiverMessage> ReceiverMessage::Parse(const Json::Value& value) { case Type::kRpc: { std::string encoded_rpc; std::vector<uint8_t> rpc; - if (json::ParseAndValidateString(value[kRpcMessageBody], &encoded_rpc) && + if (json::TryParseString(value[kRpcMessageBody], &encoded_rpc) && base64::Decode(encoded_rpc, &rpc)) { message.body = std::move(rpc); message.valid = true; diff --git a/cast/streaming/resolution.cc b/cast/streaming/resolution.cc index de6b9358..0bcf5067 100644 --- a/cast/streaming/resolution.cc +++ b/cast/streaming/resolution.cc @@ -41,9 +41,9 @@ bool FrameRateEquals(double a, double b) { } // namespace -bool Resolution::ParseAndValidate(const Json::Value& root, Resolution* out) { - if (!json::ParseAndValidateInt(root[kWidth], &(out->width)) || - !json::ParseAndValidateInt(root[kHeight], &(out->height))) { +bool Resolution::TryParse(const Json::Value& root, Resolution* out) { + if (!json::TryParseInt(root[kWidth], &(out->width)) || + !json::TryParseInt(root[kHeight], &(out->height))) { return false; } return out->IsValid(); @@ -70,12 +70,11 @@ bool Resolution::operator!=(const Resolution& other) const { return !(*this == other); } -bool Dimensions::ParseAndValidate(const Json::Value& root, Dimensions* out) { - if (!json::ParseAndValidateInt(root[kWidth], &(out->width)) || - !json::ParseAndValidateInt(root[kHeight], &(out->height)) || +bool Dimensions::TryParse(const Json::Value& root, Dimensions* out) { + if (!json::TryParseInt(root[kWidth], &(out->width)) || + !json::TryParseInt(root[kHeight], &(out->height)) || !(root[kFrameRate].isNull() || - json::ParseAndValidateSimpleFraction(root[kFrameRate], - &(out->frame_rate)))) { + json::TryParseSimpleFraction(root[kFrameRate], &(out->frame_rate)))) { return false; } return out->IsValid(); diff --git a/cast/streaming/resolution.h b/cast/streaming/resolution.h index d27c7d28..ba370820 100644 --- a/cast/streaming/resolution.h +++ b/cast/streaming/resolution.h @@ -19,7 +19,7 @@ namespace cast { // A resolution in pixels. struct Resolution { - static bool ParseAndValidate(const Json::Value& value, Resolution* out); + static bool TryParse(const Json::Value& value, Resolution* out); bool IsValid() const; Json::Value ToJson() const; @@ -33,7 +33,7 @@ struct Resolution { // A resolution in pixels and a frame rate. struct Dimensions { - static bool ParseAndValidate(const Json::Value& value, Dimensions* out); + static bool TryParse(const Json::Value& value, Dimensions* out); bool IsValid() const; Json::Value ToJson() const; diff --git a/cast/streaming/sender_message.cc b/cast/streaming/sender_message.cc index d776c67e..2526f96a 100644 --- a/cast/streaming/sender_message.cc +++ b/cast/streaming/sender_message.cc @@ -25,7 +25,7 @@ EnumNameTable<SenderMessage::Type, 4> kMessageTypeNames{ SenderMessage::Type GetMessageType(const Json::Value& root) { std::string type; - if (!json::ParseAndValidateString(root[kMessageType], &type)) { + if (!json::TryParseString(root[kMessageType], &type)) { return SenderMessage::Type::kUnknown; } @@ -44,17 +44,16 @@ ErrorOr<SenderMessage> SenderMessage::Parse(const Json::Value& value) { } SenderMessage message; - if (!json::ParseAndValidateInt(value[kSequenceNumber], - &(message.sequence_number))) { + if (!json::TryParseInt(value[kSequenceNumber], &(message.sequence_number))) { message.sequence_number = -1; } message.type = GetMessageType(value); switch (message.type) { case Type::kOffer: { - ErrorOr<Offer> offer = Offer::Parse(value[kOfferMessageBody]); - if (offer.is_value()) { - message.body = std::move(offer.value()); + Offer offer; + if (Offer::TryParse(value[kOfferMessageBody], &offer).ok()) { + message.body = std::move(offer); message.valid = true; } } break; @@ -62,7 +61,7 @@ ErrorOr<SenderMessage> SenderMessage::Parse(const Json::Value& value) { case Type::kRpc: { std::string rpc_body; std::vector<uint8_t> rpc; - if (json::ParseAndValidateString(value[kRpcMessageBody], &rpc_body) && + if (json::TryParseString(value[kRpcMessageBody], &rpc_body) && base64::Decode(rpc_body, &rpc)) { message.body = rpc; message.valid = true; diff --git a/cast/streaming/session_messager.cc b/cast/streaming/session_messager.cc index 184da30c..e66e984f 100644 --- a/cast/streaming/session_messager.cc +++ b/cast/streaming/session_messager.cc @@ -147,8 +147,8 @@ void SenderSessionMessager::OnMessage(const std::string& source_id, } int sequence_number; - if (!json::ParseAndValidateInt(message_body.value()[kSequenceNumber], - &sequence_number)) { + if (!json::TryParseInt(message_body.value()[kSequenceNumber], + &sequence_number)) { OSP_DLOG_WARN << "Received a message without a sequence number"; return; } |