aboutsummaryrefslogtreecommitdiff
path: root/cast/streaming/offer_messages.cc
diff options
context:
space:
mode:
Diffstat (limited to 'cast/streaming/offer_messages.cc')
-rw-r--r--cast/streaming/offer_messages.cc577
1 files changed, 285 insertions, 292 deletions
diff --git a/cast/streaming/offer_messages.cc b/cast/streaming/offer_messages.cc
index cea500cd..a162f09f 100644
--- a/cast/streaming/offer_messages.cc
+++ b/cast/streaming/offer_messages.cc
@@ -14,7 +14,6 @@
#include "absl/strings/match.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_split.h"
-#include "cast/streaming/capture_recommendations.h"
#include "cast/streaming/constants.h"
#include "platform/base/error.h"
#include "util/big_endian.h"
@@ -34,36 +33,78 @@ 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();
+bool CodecParameterIsValid(VideoCodec codec,
+ const std::string& codec_parameter) {
+ if (codec_parameter.empty()) {
+ return true;
+ }
+ switch (codec) {
+ case VideoCodec::kVp8:
+ return absl::StartsWith(codec_parameter, "vp08");
+ case VideoCodec::kVp9:
+ return absl::StartsWith(codec_parameter, "vp09");
+ case VideoCodec::kAv1:
+ return absl::StartsWith(codec_parameter, "av01");
+ case VideoCodec::kHevc:
+ return absl::StartsWith(codec_parameter, "hev1");
+ case VideoCodec::kH264:
+ return absl::StartsWith(codec_parameter, "avc1");
+ case VideoCodec::kNotSpecified:
+ return false;
+ }
+ OSP_NOTREACHED();
+}
+
+bool CodecParameterIsValid(AudioCodec codec,
+ const std::string& codec_parameter) {
+ if (codec_parameter.empty()) {
+ return true;
+ }
+ switch (codec) {
+ case AudioCodec::kAac:
+ return absl::StartsWith(codec_parameter, "mp4a.");
+
+ // Opus doesn't use codec parameters.
+ case AudioCodec::kOpus: // fallthrough
+ case AudioCodec::kNotSpecified:
+ return false;
}
+ OSP_NOTREACHED();
+}
+
+EnumNameTable<CastMode, 2> kCastModeNames{
+ {{"mirroring", CastMode::kMirroring}, {"remoting", CastMode::kRemoting}}};
- uint8_t t_small = t.value();
- if (t_small != t.value() || !IsRtpPayloadType(t_small)) {
- return Error(Error::Code::kParameterInvalid,
- "Received invalid RTP Payload Type.");
+bool TryParseRtpPayloadType(const Json::Value& value, RtpPayloadType* out) {
+ int t;
+ if (!json::TryParseInt(value, &t)) {
+ return false;
}
- return static_cast<RtpPayloadType>(t_small);
+ uint8_t t_small = t;
+ if (t_small != t || !IsRtpPayloadType(t_small)) {
+ return false;
+ }
+
+ *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");
+ fraction.value().numerator() != 1) {
+ 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.
@@ -71,226 +112,118 @@ 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;
- }
- 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(capture_recommendations::kDefaultAudioMinSampleRate,
- kRtpVideoTimebase) ||
- rtp_timebase.value() > kRtpVideoTimebase) {
- return json::CreateParameterError("rtp_timebase (sample rate)");
+ WriteBigEndian(quads[0], out->data());
+ WriteBigEndian(quads[1], out->data() + 8);
+ return true;
}
- 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;
- }
- }
-
- 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()};
+ return false;
}
-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, "codecName");
- 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");
+absl::string_view ToString(Stream::Type type) {
+ switch (type) {
+ case Stream::Type::kAudioSource:
+ return kAudioSourceType;
+ case Stream::Type::kVideoSource:
+ return kVideoSourceType;
+ default: {
+ OSP_NOTREACHED();
+ }
}
- return AudioStream{stream.value(), codec.value(), bit_rate.value()};
}
-ErrorOr<Resolution> ParseResolution(const Json::Value& value) {
- auto width = json::ParseInt(value, "width");
- if (!width) {
- return width.error();
- }
- auto height = json::ParseInt(value, "height");
- if (!height) {
- return height.error();
- }
- if (width.value() <= 0 || height.value() <= 0) {
- return json::CreateParameterError("resolution");
- }
- return Resolution{width.value(), height.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) {
- auto r = ParseResolution(value[i]);
- if (!r) {
- return r.error();
+ Resolution resolution;
+ if (!Resolution::TryParse(value[i], &resolution)) {
+ out->clear();
+ return false;
}
- resolutions.push_back(r.value());
+ 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, "codecName");
- 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({})};
-}
+ json::TryParseBool(value["receiverRtcpEventLog"],
+ &out->receiver_rtcp_event_log);
+ json::TryParseString(value["receiverRtcpDscp"], &out->receiver_rtcp_dscp);
+ json::TryParseString(value["codecParameter"], &out->codec_parameter);
-absl::string_view ToString(Stream::Type type) {
- switch (type) {
- case Stream::Type::kAudioSource:
- return kAudioSourceType;
- case Stream::Type::kVideoSource:
- return kVideoSourceType;
- default: {
- OSP_NOTREACHED();
- }
- }
+ return Error::None();
}
-EnumNameTable<CastMode, 2> kCastModeNames{
- {{"mirroring", CastMode::kMirroring}, {"remoting", CastMode::kRemoting}}};
-
-} // namespace
-
-ErrorOr<Json::Value> Stream::ToJson() const {
- if (channels < 1 || index < 0 || target_delay.count() <= 0 ||
- target_delay.count() > std::numeric_limits<int>::max() ||
- rtp_timebase < 1) {
- return json::CreateParameterError("Stream");
- }
+Json::Value Stream::ToJson() const {
+ OSP_DCHECK(IsValid());
Json::Value root;
root["index"] = index;
@@ -304,152 +237,212 @@ ErrorOr<Json::Value> Stream::ToJson() const {
"this code assumes Ssrc fits in a Json::UInt");
root["ssrc"] = static_cast<Json::UInt>(ssrc);
root["targetDelay"] = static_cast<int>(target_delay.count());
- root["aesKey"] = HexEncode(aes_key);
- root["aesIvMask"] = HexEncode(aes_iv_mask);
+ root["aesKey"] = HexEncode(aes_key.data(), aes_key.size());
+ root["aesIvMask"] = HexEncode(aes_iv_mask.data(), aes_iv_mask.size());
root["receiverRtcpEventLog"] = receiver_rtcp_event_log;
root["receiverRtcpDscp"] = receiver_rtcp_dscp;
root["timeBase"] = "1/" + std::to_string(rtp_timebase);
+ root["codecParameter"] = codec_parameter;
return root;
}
-ErrorOr<Json::Value> AudioStream::ToJson() const {
- // A bit rate of 0 is valid for some codec types, so we don't enforce here.
- if (bit_rate < 0) {
- return json::CreateParameterError("AudioStream");
+bool Stream::IsValid() const {
+ return channels >= 1 && index >= 0 && target_delay.count() > 0 &&
+ target_delay.count() <= std::numeric_limits<int>::max() &&
+ 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;
}
- auto error_or_stream = stream.ToJson();
- if (error_or_stream.is_error()) {
- return error_or_stream;
+ 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();
+ if (!CodecParameterIsValid(codec.value(), out->stream.codec_parameter)) {
+ return Error(Error::Code::kInvalidCodecParameter,
+ StringPrintf("Invalid audio codec parameter (%s for codec %s)",
+ out->stream.codec_parameter.c_str(),
+ CodecToString(codec.value())));
+ }
+ return Error::None();
+}
+
+Json::Value AudioStream::ToJson() const {
+ OSP_DCHECK(IsValid());
+
+ Json::Value out = stream.ToJson();
+ out[kCodecName] = CodecToString(codec);
+ out["bitRate"] = bit_rate;
+ return out;
+}
- error_or_stream.value()["codecName"] = CodecToString(codec);
- error_or_stream.value()["bitRate"] = bit_rate;
- return error_or_stream;
+bool AudioStream::IsValid() const {
+ return bit_rate >= 0 && stream.IsValid();
}
-ErrorOr<Json::Value> Resolution::ToJson() const {
- if (width <= 0 || height <= 0) {
- return json::CreateParameterError("Resolution");
+Error VideoStream::TryParse(const Json::Value& value, VideoStream* out) {
+ Error error =
+ Stream::TryParse(value, Stream::Type::kVideoSource, &out->stream);
+ if (!error.ok()) {
+ return error;
}
- Json::Value root;
- root["width"] = width;
- root["height"] = height;
- return root;
-}
+ 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();
+ if (!CodecParameterIsValid(codec.value(), out->stream.codec_parameter)) {
+ return Error(Error::Code::kInvalidCodecParameter,
+ StringPrintf("Invalid video codec parameter (%s for codec %s)",
+ out->stream.codec_parameter.c_str(),
+ CodecToString(codec.value())));
+ }
-ErrorOr<Json::Value> VideoStream::ToJson() const {
- if (max_bit_rate <= 0 || !max_frame_rate.is_positive()) {
- return json::CreateParameterError("VideoStream");
+ 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();
+ }
}
- auto error_or_stream = stream.ToJson();
- if (error_or_stream.is_error()) {
- return error_or_stream;
+ 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;
}
- auto& stream = error_or_stream.value();
- stream["codecName"] = CodecToString(codec);
- stream["maxFrameRate"] = max_frame_rate.ToString();
- stream["maxBitRate"] = max_bit_rate;
- stream["protection"] = protection;
- stream["profile"] = profile;
- stream["level"] = level;
- stream["errorRecoveryMode"] = error_recovery_mode;
+ return Error::None();
+}
+
+Json::Value VideoStream::ToJson() const {
+ OSP_DCHECK(IsValid());
+
+ Json::Value out = stream.ToJson();
+ out["codecName"] = CodecToString(codec);
+ out["maxFrameRate"] = max_frame_rate.ToString();
+ out["maxBitRate"] = max_bit_rate;
+ out["protection"] = protection;
+ out["profile"] = profile;
+ out["level"] = level;
+ out["errorRecoveryMode"] = error_recovery_mode;
Json::Value rs;
for (auto resolution : resolutions) {
- auto eoj = resolution.ToJson();
- if (eoj.is_error()) {
- return eoj;
- }
- rs.append(eoj.value());
+ rs.append(resolution.ToJson());
}
- stream["resolutions"] = std::move(rs);
- return error_or_stream;
+ out["resolutions"] = std::move(rs);
+ return out;
+}
+
+bool VideoStream::IsValid() const {
+ return max_bit_rate > 0 && max_frame_rate.is_positive();
}
// static
ErrorOr<Offer> Offer::Parse(const Json::Value& root) {
+ Offer out;
+ Error error = TryParse(root, &out);
+ return error.ok() ? ErrorOr<Offer>(std::move(out))
+ : ErrorOr<Offer>(std::move(error));
+}
+
+// static
+Error Offer::TryParse(const Json::Value& root, Offer* out) {
if (!root.isObject()) {
- return json::CreateParseError("null offer");
+ return Error(Error::Code::kJsonParseError, "null offer");
}
- ErrorOr<CastMode> cast_mode =
+ const ErrorOr<CastMode> cast_mode =
GetEnum(kCastModeNames, root["castMode"].asString());
- const ErrorOr<bool> get_status = json::ParseBool(root, "receiverGetStatus");
-
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));
+ }
+ } else if (type == kVideoSourceType) {
+ VideoStream stream;
+ error = VideoStream::TryParse(fields, &stream);
+ if (error.ok()) {
+ video_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();
- }
+ }
+
+ if (!error.ok()) {
+ if (error.code() == Error::Code::kUnknownCodec) {
+ OSP_VLOG << "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), get_status.value({}),
- std::move(audio_streams), std::move(video_streams)};
+ *out = Offer{cast_mode.value(CastMode::kMirroring), std::move(audio_streams),
+ std::move(video_streams)};
+ return Error::None();
}
-ErrorOr<Json::Value> Offer::ToJson() const {
+Json::Value Offer::ToJson() const {
+ OSP_DCHECK(IsValid());
Json::Value root;
-
root["castMode"] = GetEnumName(kCastModeNames, cast_mode).value();
- root["receiverGetStatus"] = supports_wifi_status_reporting;
-
Json::Value streams;
- for (auto& as : audio_streams) {
- auto eoj = as.ToJson();
- if (eoj.is_error()) {
- return eoj;
- }
- streams.append(eoj.value());
+ for (auto& stream : audio_streams) {
+ streams.append(stream.ToJson());
}
- for (auto& vs : video_streams) {
- auto eoj = vs.ToJson();
- if (eoj.is_error()) {
- return eoj;
- }
- streams.append(eoj.value());
+ for (auto& stream : video_streams) {
+ streams.append(stream.ToJson());
}
root[kSupportedStreams] = std::move(streams);
return root;
}
+bool Offer::IsValid() const {
+ return std::all_of(audio_streams.begin(), audio_streams.end(),
+ [](const AudioStream& a) { return a.IsValid(); }) &&
+ std::all_of(video_streams.begin(), video_streams.end(),
+ [](const VideoStream& v) { return v.IsValid(); });
+}
} // namespace cast
} // namespace openscreen