xref: /aosp_15_r20/external/openscreen/cast/streaming/offer_messages.cc (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
1*3f982cf4SFabien Sanglard // Copyright 2019 The Chromium Authors. All rights reserved.
2*3f982cf4SFabien Sanglard // Use of this source code is governed by a BSD-style license that can be
3*3f982cf4SFabien Sanglard // found in the LICENSE file.
4*3f982cf4SFabien Sanglard 
5*3f982cf4SFabien Sanglard #include "cast/streaming/offer_messages.h"
6*3f982cf4SFabien Sanglard 
7*3f982cf4SFabien Sanglard #include <inttypes.h>
8*3f982cf4SFabien Sanglard 
9*3f982cf4SFabien Sanglard #include <algorithm>
10*3f982cf4SFabien Sanglard #include <limits>
11*3f982cf4SFabien Sanglard #include <string>
12*3f982cf4SFabien Sanglard #include <utility>
13*3f982cf4SFabien Sanglard 
14*3f982cf4SFabien Sanglard #include "absl/strings/match.h"
15*3f982cf4SFabien Sanglard #include "absl/strings/numbers.h"
16*3f982cf4SFabien Sanglard #include "absl/strings/str_split.h"
17*3f982cf4SFabien Sanglard #include "cast/streaming/constants.h"
18*3f982cf4SFabien Sanglard #include "platform/base/error.h"
19*3f982cf4SFabien Sanglard #include "util/big_endian.h"
20*3f982cf4SFabien Sanglard #include "util/enum_name_table.h"
21*3f982cf4SFabien Sanglard #include "util/json/json_helpers.h"
22*3f982cf4SFabien Sanglard #include "util/json/json_serialization.h"
23*3f982cf4SFabien Sanglard #include "util/osp_logging.h"
24*3f982cf4SFabien Sanglard #include "util/stringprintf.h"
25*3f982cf4SFabien Sanglard 
26*3f982cf4SFabien Sanglard namespace openscreen {
27*3f982cf4SFabien Sanglard namespace cast {
28*3f982cf4SFabien Sanglard 
29*3f982cf4SFabien Sanglard namespace {
30*3f982cf4SFabien Sanglard 
31*3f982cf4SFabien Sanglard constexpr char kSupportedStreams[] = "supportedStreams";
32*3f982cf4SFabien Sanglard constexpr char kAudioSourceType[] = "audio_source";
33*3f982cf4SFabien Sanglard constexpr char kVideoSourceType[] = "video_source";
34*3f982cf4SFabien Sanglard constexpr char kStreamType[] = "type";
35*3f982cf4SFabien Sanglard 
CodecParameterIsValid(VideoCodec codec,const std::string & codec_parameter)36*3f982cf4SFabien Sanglard bool CodecParameterIsValid(VideoCodec codec,
37*3f982cf4SFabien Sanglard                            const std::string& codec_parameter) {
38*3f982cf4SFabien Sanglard   if (codec_parameter.empty()) {
39*3f982cf4SFabien Sanglard     return true;
40*3f982cf4SFabien Sanglard   }
41*3f982cf4SFabien Sanglard   switch (codec) {
42*3f982cf4SFabien Sanglard     case VideoCodec::kVp8:
43*3f982cf4SFabien Sanglard       return absl::StartsWith(codec_parameter, "vp08");
44*3f982cf4SFabien Sanglard     case VideoCodec::kVp9:
45*3f982cf4SFabien Sanglard       return absl::StartsWith(codec_parameter, "vp09");
46*3f982cf4SFabien Sanglard     case VideoCodec::kAv1:
47*3f982cf4SFabien Sanglard       return absl::StartsWith(codec_parameter, "av01");
48*3f982cf4SFabien Sanglard     case VideoCodec::kHevc:
49*3f982cf4SFabien Sanglard       return absl::StartsWith(codec_parameter, "hev1");
50*3f982cf4SFabien Sanglard     case VideoCodec::kH264:
51*3f982cf4SFabien Sanglard       return absl::StartsWith(codec_parameter, "avc1");
52*3f982cf4SFabien Sanglard     case VideoCodec::kNotSpecified:
53*3f982cf4SFabien Sanglard       return false;
54*3f982cf4SFabien Sanglard   }
55*3f982cf4SFabien Sanglard   OSP_NOTREACHED();
56*3f982cf4SFabien Sanglard }
57*3f982cf4SFabien Sanglard 
CodecParameterIsValid(AudioCodec codec,const std::string & codec_parameter)58*3f982cf4SFabien Sanglard bool CodecParameterIsValid(AudioCodec codec,
59*3f982cf4SFabien Sanglard                            const std::string& codec_parameter) {
60*3f982cf4SFabien Sanglard   if (codec_parameter.empty()) {
61*3f982cf4SFabien Sanglard     return true;
62*3f982cf4SFabien Sanglard   }
63*3f982cf4SFabien Sanglard   switch (codec) {
64*3f982cf4SFabien Sanglard     case AudioCodec::kAac:
65*3f982cf4SFabien Sanglard       return absl::StartsWith(codec_parameter, "mp4a.");
66*3f982cf4SFabien Sanglard 
67*3f982cf4SFabien Sanglard     // Opus doesn't use codec parameters.
68*3f982cf4SFabien Sanglard     case AudioCodec::kOpus:  // fallthrough
69*3f982cf4SFabien Sanglard     case AudioCodec::kNotSpecified:
70*3f982cf4SFabien Sanglard       return false;
71*3f982cf4SFabien Sanglard   }
72*3f982cf4SFabien Sanglard   OSP_NOTREACHED();
73*3f982cf4SFabien Sanglard }
74*3f982cf4SFabien Sanglard 
75*3f982cf4SFabien Sanglard EnumNameTable<CastMode, 2> kCastModeNames{
76*3f982cf4SFabien Sanglard     {{"mirroring", CastMode::kMirroring}, {"remoting", CastMode::kRemoting}}};
77*3f982cf4SFabien Sanglard 
TryParseRtpPayloadType(const Json::Value & value,RtpPayloadType * out)78*3f982cf4SFabien Sanglard bool TryParseRtpPayloadType(const Json::Value& value, RtpPayloadType* out) {
79*3f982cf4SFabien Sanglard   int t;
80*3f982cf4SFabien Sanglard   if (!json::TryParseInt(value, &t)) {
81*3f982cf4SFabien Sanglard     return false;
82*3f982cf4SFabien Sanglard   }
83*3f982cf4SFabien Sanglard 
84*3f982cf4SFabien Sanglard   uint8_t t_small = t;
85*3f982cf4SFabien Sanglard   if (t_small != t || !IsRtpPayloadType(t_small)) {
86*3f982cf4SFabien Sanglard     return false;
87*3f982cf4SFabien Sanglard   }
88*3f982cf4SFabien Sanglard 
89*3f982cf4SFabien Sanglard   *out = static_cast<RtpPayloadType>(t_small);
90*3f982cf4SFabien Sanglard   return true;
91*3f982cf4SFabien Sanglard }
92*3f982cf4SFabien Sanglard 
TryParseRtpTimebase(const Json::Value & value,int * out)93*3f982cf4SFabien Sanglard bool TryParseRtpTimebase(const Json::Value& value, int* out) {
94*3f982cf4SFabien Sanglard   std::string raw_timebase;
95*3f982cf4SFabien Sanglard   if (!json::TryParseString(value, &raw_timebase)) {
96*3f982cf4SFabien Sanglard     return false;
97*3f982cf4SFabien Sanglard   }
98*3f982cf4SFabien Sanglard 
99*3f982cf4SFabien Sanglard   // The spec demands a leading 1, so this isn't really a fraction.
100*3f982cf4SFabien Sanglard   const auto fraction = SimpleFraction::FromString(raw_timebase);
101*3f982cf4SFabien Sanglard   if (fraction.is_error() || !fraction.value().is_positive() ||
102*3f982cf4SFabien Sanglard       fraction.value().numerator() != 1) {
103*3f982cf4SFabien Sanglard     return false;
104*3f982cf4SFabien Sanglard   }
105*3f982cf4SFabien Sanglard 
106*3f982cf4SFabien Sanglard   *out = fraction.value().denominator();
107*3f982cf4SFabien Sanglard   return true;
108*3f982cf4SFabien Sanglard }
109*3f982cf4SFabien Sanglard 
110*3f982cf4SFabien Sanglard // For a hex byte, the conversion is 4 bits to 1 character, e.g.
111*3f982cf4SFabien Sanglard // 0b11110001 becomes F1, so 1 byte is two characters.
112*3f982cf4SFabien Sanglard constexpr int kHexDigitsPerByte = 2;
113*3f982cf4SFabien Sanglard constexpr int kAesBytesSize = 16;
114*3f982cf4SFabien Sanglard constexpr int kAesStringLength = kAesBytesSize * kHexDigitsPerByte;
TryParseAesHexBytes(const Json::Value & value,std::array<uint8_t,kAesBytesSize> * out)115*3f982cf4SFabien Sanglard bool TryParseAesHexBytes(const Json::Value& value,
116*3f982cf4SFabien Sanglard                          std::array<uint8_t, kAesBytesSize>* out) {
117*3f982cf4SFabien Sanglard   std::string hex_string;
118*3f982cf4SFabien Sanglard   if (!json::TryParseString(value, &hex_string)) {
119*3f982cf4SFabien Sanglard     return false;
120*3f982cf4SFabien Sanglard   }
121*3f982cf4SFabien Sanglard 
122*3f982cf4SFabien Sanglard   constexpr int kHexDigitsPerScanField = 16;
123*3f982cf4SFabien Sanglard   constexpr int kNumScanFields = kAesStringLength / kHexDigitsPerScanField;
124*3f982cf4SFabien Sanglard   uint64_t quads[kNumScanFields];
125*3f982cf4SFabien Sanglard   int chars_scanned;
126*3f982cf4SFabien Sanglard   if (hex_string.size() == kAesStringLength &&
127*3f982cf4SFabien Sanglard       sscanf(hex_string.c_str(), "%16" SCNx64 "%16" SCNx64 "%n", &quads[0],
128*3f982cf4SFabien Sanglard              &quads[1], &chars_scanned) == kNumScanFields &&
129*3f982cf4SFabien Sanglard       chars_scanned == kAesStringLength &&
130*3f982cf4SFabien Sanglard       std::none_of(hex_string.begin(), hex_string.end(),
131*3f982cf4SFabien Sanglard                    [](char c) { return std::isspace(c); })) {
132*3f982cf4SFabien Sanglard     WriteBigEndian(quads[0], out->data());
133*3f982cf4SFabien Sanglard     WriteBigEndian(quads[1], out->data() + 8);
134*3f982cf4SFabien Sanglard     return true;
135*3f982cf4SFabien Sanglard   }
136*3f982cf4SFabien Sanglard 
137*3f982cf4SFabien Sanglard   return false;
138*3f982cf4SFabien Sanglard }
139*3f982cf4SFabien Sanglard 
ToString(Stream::Type type)140*3f982cf4SFabien Sanglard absl::string_view ToString(Stream::Type type) {
141*3f982cf4SFabien Sanglard   switch (type) {
142*3f982cf4SFabien Sanglard     case Stream::Type::kAudioSource:
143*3f982cf4SFabien Sanglard       return kAudioSourceType;
144*3f982cf4SFabien Sanglard     case Stream::Type::kVideoSource:
145*3f982cf4SFabien Sanglard       return kVideoSourceType;
146*3f982cf4SFabien Sanglard     default: {
147*3f982cf4SFabien Sanglard       OSP_NOTREACHED();
148*3f982cf4SFabien Sanglard     }
149*3f982cf4SFabien Sanglard   }
150*3f982cf4SFabien Sanglard }
151*3f982cf4SFabien Sanglard 
TryParseResolutions(const Json::Value & value,std::vector<Resolution> * out)152*3f982cf4SFabien Sanglard bool TryParseResolutions(const Json::Value& value,
153*3f982cf4SFabien Sanglard                          std::vector<Resolution>* out) {
154*3f982cf4SFabien Sanglard   out->clear();
155*3f982cf4SFabien Sanglard 
156*3f982cf4SFabien Sanglard   // Some legacy senders don't provide resolutions, so just return empty.
157*3f982cf4SFabien Sanglard   if (!value.isArray() || value.empty()) {
158*3f982cf4SFabien Sanglard     return false;
159*3f982cf4SFabien Sanglard   }
160*3f982cf4SFabien Sanglard 
161*3f982cf4SFabien Sanglard   for (Json::ArrayIndex i = 0; i < value.size(); ++i) {
162*3f982cf4SFabien Sanglard     Resolution resolution;
163*3f982cf4SFabien Sanglard     if (!Resolution::TryParse(value[i], &resolution)) {
164*3f982cf4SFabien Sanglard       out->clear();
165*3f982cf4SFabien Sanglard       return false;
166*3f982cf4SFabien Sanglard     }
167*3f982cf4SFabien Sanglard     out->push_back(std::move(resolution));
168*3f982cf4SFabien Sanglard   }
169*3f982cf4SFabien Sanglard 
170*3f982cf4SFabien Sanglard   return true;
171*3f982cf4SFabien Sanglard }
172*3f982cf4SFabien Sanglard 
173*3f982cf4SFabien Sanglard }  // namespace
174*3f982cf4SFabien Sanglard 
TryParse(const Json::Value & value,Stream::Type type,Stream * out)175*3f982cf4SFabien Sanglard Error Stream::TryParse(const Json::Value& value,
176*3f982cf4SFabien Sanglard                        Stream::Type type,
177*3f982cf4SFabien Sanglard                        Stream* out) {
178*3f982cf4SFabien Sanglard   out->type = type;
179*3f982cf4SFabien Sanglard 
180*3f982cf4SFabien Sanglard   if (!json::TryParseInt(value["index"], &out->index) ||
181*3f982cf4SFabien Sanglard       !json::TryParseUint(value["ssrc"], &out->ssrc) ||
182*3f982cf4SFabien Sanglard       !TryParseRtpPayloadType(value["rtpPayloadType"],
183*3f982cf4SFabien Sanglard                               &out->rtp_payload_type) ||
184*3f982cf4SFabien Sanglard       !TryParseRtpTimebase(value["timeBase"], &out->rtp_timebase)) {
185*3f982cf4SFabien Sanglard     return Error(Error::Code::kJsonParseError,
186*3f982cf4SFabien Sanglard                  "Offer stream has missing or invalid mandatory field");
187*3f982cf4SFabien Sanglard   }
188*3f982cf4SFabien Sanglard 
189*3f982cf4SFabien Sanglard   if (!json::TryParseInt(value["channels"], &out->channels)) {
190*3f982cf4SFabien Sanglard     out->channels = out->type == Stream::Type::kAudioSource
191*3f982cf4SFabien Sanglard                         ? kDefaultNumAudioChannels
192*3f982cf4SFabien Sanglard                         : kDefaultNumVideoChannels;
193*3f982cf4SFabien Sanglard   } else if (out->channels <= 0) {
194*3f982cf4SFabien Sanglard     return Error(Error::Code::kJsonParseError, "Invalid channel count");
195*3f982cf4SFabien Sanglard   }
196*3f982cf4SFabien Sanglard 
197*3f982cf4SFabien Sanglard   if (!TryParseAesHexBytes(value["aesKey"], &out->aes_key) ||
198*3f982cf4SFabien Sanglard       !TryParseAesHexBytes(value["aesIvMask"], &out->aes_iv_mask)) {
199*3f982cf4SFabien Sanglard     return Error(Error::Code::kUnencryptedOffer,
200*3f982cf4SFabien Sanglard                  "Offer stream must have both a valid aesKey and aesIvMask");
201*3f982cf4SFabien Sanglard   }
202*3f982cf4SFabien Sanglard   if (out->rtp_timebase <
203*3f982cf4SFabien Sanglard           std::min(kDefaultAudioMinSampleRate, kRtpVideoTimebase) ||
204*3f982cf4SFabien Sanglard       out->rtp_timebase > kRtpVideoTimebase) {
205*3f982cf4SFabien Sanglard     return Error(Error::Code::kJsonParseError, "rtp_timebase (sample rate)");
206*3f982cf4SFabien Sanglard   }
207*3f982cf4SFabien Sanglard 
208*3f982cf4SFabien Sanglard   out->target_delay = kDefaultTargetPlayoutDelay;
209*3f982cf4SFabien Sanglard   int target_delay;
210*3f982cf4SFabien Sanglard   if (json::TryParseInt(value["targetDelay"], &target_delay)) {
211*3f982cf4SFabien Sanglard     auto d = std::chrono::milliseconds(target_delay);
212*3f982cf4SFabien Sanglard     if (kMinTargetPlayoutDelay <= d && d <= kMaxTargetPlayoutDelay) {
213*3f982cf4SFabien Sanglard       out->target_delay = d;
214*3f982cf4SFabien Sanglard     }
215*3f982cf4SFabien Sanglard   }
216*3f982cf4SFabien Sanglard 
217*3f982cf4SFabien Sanglard   json::TryParseBool(value["receiverRtcpEventLog"],
218*3f982cf4SFabien Sanglard                      &out->receiver_rtcp_event_log);
219*3f982cf4SFabien Sanglard   json::TryParseString(value["receiverRtcpDscp"], &out->receiver_rtcp_dscp);
220*3f982cf4SFabien Sanglard   json::TryParseString(value["codecParameter"], &out->codec_parameter);
221*3f982cf4SFabien Sanglard 
222*3f982cf4SFabien Sanglard   return Error::None();
223*3f982cf4SFabien Sanglard }
224*3f982cf4SFabien Sanglard 
ToJson() const225*3f982cf4SFabien Sanglard Json::Value Stream::ToJson() const {
226*3f982cf4SFabien Sanglard   OSP_DCHECK(IsValid());
227*3f982cf4SFabien Sanglard 
228*3f982cf4SFabien Sanglard   Json::Value root;
229*3f982cf4SFabien Sanglard   root["index"] = index;
230*3f982cf4SFabien Sanglard   root["type"] = std::string(ToString(type));
231*3f982cf4SFabien Sanglard   root["channels"] = channels;
232*3f982cf4SFabien Sanglard   root["rtpPayloadType"] = static_cast<int>(rtp_payload_type);
233*3f982cf4SFabien Sanglard   // rtpProfile is technically required by the spec, although it is always set
234*3f982cf4SFabien Sanglard   // to cast. We set it here to be compliant with all spec implementers.
235*3f982cf4SFabien Sanglard   root["rtpProfile"] = "cast";
236*3f982cf4SFabien Sanglard   static_assert(sizeof(ssrc) <= sizeof(Json::UInt),
237*3f982cf4SFabien Sanglard                 "this code assumes Ssrc fits in a Json::UInt");
238*3f982cf4SFabien Sanglard   root["ssrc"] = static_cast<Json::UInt>(ssrc);
239*3f982cf4SFabien Sanglard   root["targetDelay"] = static_cast<int>(target_delay.count());
240*3f982cf4SFabien Sanglard   root["aesKey"] = HexEncode(aes_key.data(), aes_key.size());
241*3f982cf4SFabien Sanglard   root["aesIvMask"] = HexEncode(aes_iv_mask.data(), aes_iv_mask.size());
242*3f982cf4SFabien Sanglard   root["receiverRtcpEventLog"] = receiver_rtcp_event_log;
243*3f982cf4SFabien Sanglard   root["receiverRtcpDscp"] = receiver_rtcp_dscp;
244*3f982cf4SFabien Sanglard   root["timeBase"] = "1/" + std::to_string(rtp_timebase);
245*3f982cf4SFabien Sanglard   root["codecParameter"] = codec_parameter;
246*3f982cf4SFabien Sanglard   return root;
247*3f982cf4SFabien Sanglard }
248*3f982cf4SFabien Sanglard 
IsValid() const249*3f982cf4SFabien Sanglard bool Stream::IsValid() const {
250*3f982cf4SFabien Sanglard   return channels >= 1 && index >= 0 && target_delay.count() > 0 &&
251*3f982cf4SFabien Sanglard          target_delay.count() <= std::numeric_limits<int>::max() &&
252*3f982cf4SFabien Sanglard          rtp_timebase >= 1;
253*3f982cf4SFabien Sanglard }
254*3f982cf4SFabien Sanglard 
TryParse(const Json::Value & value,AudioStream * out)255*3f982cf4SFabien Sanglard Error AudioStream::TryParse(const Json::Value& value, AudioStream* out) {
256*3f982cf4SFabien Sanglard   Error error =
257*3f982cf4SFabien Sanglard       Stream::TryParse(value, Stream::Type::kAudioSource, &out->stream);
258*3f982cf4SFabien Sanglard   if (!error.ok()) {
259*3f982cf4SFabien Sanglard     return error;
260*3f982cf4SFabien Sanglard   }
261*3f982cf4SFabien Sanglard 
262*3f982cf4SFabien Sanglard   std::string codec_name;
263*3f982cf4SFabien Sanglard   if (!json::TryParseInt(value["bitRate"], &out->bit_rate) ||
264*3f982cf4SFabien Sanglard       out->bit_rate < 0 ||
265*3f982cf4SFabien Sanglard       !json::TryParseString(value[kCodecName], &codec_name)) {
266*3f982cf4SFabien Sanglard     return Error(Error::Code::kJsonParseError, "Invalid audio stream field");
267*3f982cf4SFabien Sanglard   }
268*3f982cf4SFabien Sanglard   ErrorOr<AudioCodec> codec = StringToAudioCodec(codec_name);
269*3f982cf4SFabien Sanglard   if (!codec) {
270*3f982cf4SFabien Sanglard     return Error(Error::Code::kUnknownCodec,
271*3f982cf4SFabien Sanglard                  "Codec is not known, can't use stream");
272*3f982cf4SFabien Sanglard   }
273*3f982cf4SFabien Sanglard   out->codec = codec.value();
274*3f982cf4SFabien Sanglard   if (!CodecParameterIsValid(codec.value(), out->stream.codec_parameter)) {
275*3f982cf4SFabien Sanglard     return Error(Error::Code::kInvalidCodecParameter,
276*3f982cf4SFabien Sanglard                  StringPrintf("Invalid audio codec parameter (%s for codec %s)",
277*3f982cf4SFabien Sanglard                               out->stream.codec_parameter.c_str(),
278*3f982cf4SFabien Sanglard                               CodecToString(codec.value())));
279*3f982cf4SFabien Sanglard   }
280*3f982cf4SFabien Sanglard   return Error::None();
281*3f982cf4SFabien Sanglard }
282*3f982cf4SFabien Sanglard 
ToJson() const283*3f982cf4SFabien Sanglard Json::Value AudioStream::ToJson() const {
284*3f982cf4SFabien Sanglard   OSP_DCHECK(IsValid());
285*3f982cf4SFabien Sanglard 
286*3f982cf4SFabien Sanglard   Json::Value out = stream.ToJson();
287*3f982cf4SFabien Sanglard   out[kCodecName] = CodecToString(codec);
288*3f982cf4SFabien Sanglard   out["bitRate"] = bit_rate;
289*3f982cf4SFabien Sanglard   return out;
290*3f982cf4SFabien Sanglard }
291*3f982cf4SFabien Sanglard 
IsValid() const292*3f982cf4SFabien Sanglard bool AudioStream::IsValid() const {
293*3f982cf4SFabien Sanglard   return bit_rate >= 0 && stream.IsValid();
294*3f982cf4SFabien Sanglard }
295*3f982cf4SFabien Sanglard 
TryParse(const Json::Value & value,VideoStream * out)296*3f982cf4SFabien Sanglard Error VideoStream::TryParse(const Json::Value& value, VideoStream* out) {
297*3f982cf4SFabien Sanglard   Error error =
298*3f982cf4SFabien Sanglard       Stream::TryParse(value, Stream::Type::kVideoSource, &out->stream);
299*3f982cf4SFabien Sanglard   if (!error.ok()) {
300*3f982cf4SFabien Sanglard     return error;
301*3f982cf4SFabien Sanglard   }
302*3f982cf4SFabien Sanglard 
303*3f982cf4SFabien Sanglard   std::string codec_name;
304*3f982cf4SFabien Sanglard   if (!json::TryParseString(value[kCodecName], &codec_name)) {
305*3f982cf4SFabien Sanglard     return Error(Error::Code::kJsonParseError, "Video stream missing codec");
306*3f982cf4SFabien Sanglard   }
307*3f982cf4SFabien Sanglard   ErrorOr<VideoCodec> codec = StringToVideoCodec(codec_name);
308*3f982cf4SFabien Sanglard   if (!codec) {
309*3f982cf4SFabien Sanglard     return Error(Error::Code::kUnknownCodec,
310*3f982cf4SFabien Sanglard                  "Codec is not known, can't use stream");
311*3f982cf4SFabien Sanglard   }
312*3f982cf4SFabien Sanglard   out->codec = codec.value();
313*3f982cf4SFabien Sanglard   if (!CodecParameterIsValid(codec.value(), out->stream.codec_parameter)) {
314*3f982cf4SFabien Sanglard     return Error(Error::Code::kInvalidCodecParameter,
315*3f982cf4SFabien Sanglard                  StringPrintf("Invalid video codec parameter (%s for codec %s)",
316*3f982cf4SFabien Sanglard                               out->stream.codec_parameter.c_str(),
317*3f982cf4SFabien Sanglard                               CodecToString(codec.value())));
318*3f982cf4SFabien Sanglard   }
319*3f982cf4SFabien Sanglard 
320*3f982cf4SFabien Sanglard   out->max_frame_rate = SimpleFraction{kDefaultMaxFrameRate, 1};
321*3f982cf4SFabien Sanglard   std::string raw_max_frame_rate;
322*3f982cf4SFabien Sanglard   if (json::TryParseString(value["maxFrameRate"], &raw_max_frame_rate)) {
323*3f982cf4SFabien Sanglard     auto parsed = SimpleFraction::FromString(raw_max_frame_rate);
324*3f982cf4SFabien Sanglard     if (parsed.is_value() && parsed.value().is_positive()) {
325*3f982cf4SFabien Sanglard       out->max_frame_rate = parsed.value();
326*3f982cf4SFabien Sanglard     }
327*3f982cf4SFabien Sanglard   }
328*3f982cf4SFabien Sanglard 
329*3f982cf4SFabien Sanglard   TryParseResolutions(value["resolutions"], &out->resolutions);
330*3f982cf4SFabien Sanglard   json::TryParseString(value["profile"], &out->profile);
331*3f982cf4SFabien Sanglard   json::TryParseString(value["protection"], &out->protection);
332*3f982cf4SFabien Sanglard   json::TryParseString(value["level"], &out->level);
333*3f982cf4SFabien Sanglard   json::TryParseString(value["errorRecoveryMode"], &out->error_recovery_mode);
334*3f982cf4SFabien Sanglard   if (!json::TryParseInt(value["maxBitRate"], &out->max_bit_rate)) {
335*3f982cf4SFabien Sanglard     out->max_bit_rate = 4 << 20;
336*3f982cf4SFabien Sanglard   }
337*3f982cf4SFabien Sanglard 
338*3f982cf4SFabien Sanglard   return Error::None();
339*3f982cf4SFabien Sanglard }
340*3f982cf4SFabien Sanglard 
ToJson() const341*3f982cf4SFabien Sanglard Json::Value VideoStream::ToJson() const {
342*3f982cf4SFabien Sanglard   OSP_DCHECK(IsValid());
343*3f982cf4SFabien Sanglard 
344*3f982cf4SFabien Sanglard   Json::Value out = stream.ToJson();
345*3f982cf4SFabien Sanglard   out["codecName"] = CodecToString(codec);
346*3f982cf4SFabien Sanglard   out["maxFrameRate"] = max_frame_rate.ToString();
347*3f982cf4SFabien Sanglard   out["maxBitRate"] = max_bit_rate;
348*3f982cf4SFabien Sanglard   out["protection"] = protection;
349*3f982cf4SFabien Sanglard   out["profile"] = profile;
350*3f982cf4SFabien Sanglard   out["level"] = level;
351*3f982cf4SFabien Sanglard   out["errorRecoveryMode"] = error_recovery_mode;
352*3f982cf4SFabien Sanglard 
353*3f982cf4SFabien Sanglard   Json::Value rs;
354*3f982cf4SFabien Sanglard   for (auto resolution : resolutions) {
355*3f982cf4SFabien Sanglard     rs.append(resolution.ToJson());
356*3f982cf4SFabien Sanglard   }
357*3f982cf4SFabien Sanglard   out["resolutions"] = std::move(rs);
358*3f982cf4SFabien Sanglard   return out;
359*3f982cf4SFabien Sanglard }
360*3f982cf4SFabien Sanglard 
IsValid() const361*3f982cf4SFabien Sanglard bool VideoStream::IsValid() const {
362*3f982cf4SFabien Sanglard   return max_bit_rate > 0 && max_frame_rate.is_positive();
363*3f982cf4SFabien Sanglard }
364*3f982cf4SFabien Sanglard 
365*3f982cf4SFabien Sanglard // static
Parse(const Json::Value & root)366*3f982cf4SFabien Sanglard ErrorOr<Offer> Offer::Parse(const Json::Value& root) {
367*3f982cf4SFabien Sanglard   Offer out;
368*3f982cf4SFabien Sanglard   Error error = TryParse(root, &out);
369*3f982cf4SFabien Sanglard   return error.ok() ? ErrorOr<Offer>(std::move(out))
370*3f982cf4SFabien Sanglard                     : ErrorOr<Offer>(std::move(error));
371*3f982cf4SFabien Sanglard }
372*3f982cf4SFabien Sanglard 
373*3f982cf4SFabien Sanglard // static
TryParse(const Json::Value & root,Offer * out)374*3f982cf4SFabien Sanglard Error Offer::TryParse(const Json::Value& root, Offer* out) {
375*3f982cf4SFabien Sanglard   if (!root.isObject()) {
376*3f982cf4SFabien Sanglard     return Error(Error::Code::kJsonParseError, "null offer");
377*3f982cf4SFabien Sanglard   }
378*3f982cf4SFabien Sanglard   const ErrorOr<CastMode> cast_mode =
379*3f982cf4SFabien Sanglard       GetEnum(kCastModeNames, root["castMode"].asString());
380*3f982cf4SFabien Sanglard   Json::Value supported_streams = root[kSupportedStreams];
381*3f982cf4SFabien Sanglard   if (!supported_streams.isArray()) {
382*3f982cf4SFabien Sanglard     return Error(Error::Code::kJsonParseError, "supported streams in offer");
383*3f982cf4SFabien Sanglard   }
384*3f982cf4SFabien Sanglard 
385*3f982cf4SFabien Sanglard   std::vector<AudioStream> audio_streams;
386*3f982cf4SFabien Sanglard   std::vector<VideoStream> video_streams;
387*3f982cf4SFabien Sanglard   for (Json::ArrayIndex i = 0; i < supported_streams.size(); ++i) {
388*3f982cf4SFabien Sanglard     const Json::Value& fields = supported_streams[i];
389*3f982cf4SFabien Sanglard     std::string type;
390*3f982cf4SFabien Sanglard     if (!json::TryParseString(fields[kStreamType], &type)) {
391*3f982cf4SFabien Sanglard       return Error(Error::Code::kJsonParseError, "Missing stream type");
392*3f982cf4SFabien Sanglard     }
393*3f982cf4SFabien Sanglard 
394*3f982cf4SFabien Sanglard     Error error;
395*3f982cf4SFabien Sanglard     if (type == kAudioSourceType) {
396*3f982cf4SFabien Sanglard       AudioStream stream;
397*3f982cf4SFabien Sanglard       error = AudioStream::TryParse(fields, &stream);
398*3f982cf4SFabien Sanglard       if (error.ok()) {
399*3f982cf4SFabien Sanglard         audio_streams.push_back(std::move(stream));
400*3f982cf4SFabien Sanglard       }
401*3f982cf4SFabien Sanglard     } else if (type == kVideoSourceType) {
402*3f982cf4SFabien Sanglard       VideoStream stream;
403*3f982cf4SFabien Sanglard       error = VideoStream::TryParse(fields, &stream);
404*3f982cf4SFabien Sanglard       if (error.ok()) {
405*3f982cf4SFabien Sanglard         video_streams.push_back(std::move(stream));
406*3f982cf4SFabien Sanglard       }
407*3f982cf4SFabien Sanglard     }
408*3f982cf4SFabien Sanglard 
409*3f982cf4SFabien Sanglard     if (!error.ok()) {
410*3f982cf4SFabien Sanglard       if (error.code() == Error::Code::kUnknownCodec) {
411*3f982cf4SFabien Sanglard         OSP_VLOG << "Dropping audio stream due to unknown codec: " << error;
412*3f982cf4SFabien Sanglard         continue;
413*3f982cf4SFabien Sanglard       } else {
414*3f982cf4SFabien Sanglard         return error;
415*3f982cf4SFabien Sanglard       }
416*3f982cf4SFabien Sanglard     }
417*3f982cf4SFabien Sanglard   }
418*3f982cf4SFabien Sanglard 
419*3f982cf4SFabien Sanglard   *out = Offer{cast_mode.value(CastMode::kMirroring), std::move(audio_streams),
420*3f982cf4SFabien Sanglard                std::move(video_streams)};
421*3f982cf4SFabien Sanglard   return Error::None();
422*3f982cf4SFabien Sanglard }
423*3f982cf4SFabien Sanglard 
ToJson() const424*3f982cf4SFabien Sanglard Json::Value Offer::ToJson() const {
425*3f982cf4SFabien Sanglard   OSP_DCHECK(IsValid());
426*3f982cf4SFabien Sanglard   Json::Value root;
427*3f982cf4SFabien Sanglard   root["castMode"] = GetEnumName(kCastModeNames, cast_mode).value();
428*3f982cf4SFabien Sanglard   Json::Value streams;
429*3f982cf4SFabien Sanglard   for (auto& stream : audio_streams) {
430*3f982cf4SFabien Sanglard     streams.append(stream.ToJson());
431*3f982cf4SFabien Sanglard   }
432*3f982cf4SFabien Sanglard 
433*3f982cf4SFabien Sanglard   for (auto& stream : video_streams) {
434*3f982cf4SFabien Sanglard     streams.append(stream.ToJson());
435*3f982cf4SFabien Sanglard   }
436*3f982cf4SFabien Sanglard 
437*3f982cf4SFabien Sanglard   root[kSupportedStreams] = std::move(streams);
438*3f982cf4SFabien Sanglard   return root;
439*3f982cf4SFabien Sanglard }
440*3f982cf4SFabien Sanglard 
IsValid() const441*3f982cf4SFabien Sanglard bool Offer::IsValid() const {
442*3f982cf4SFabien Sanglard   return std::all_of(audio_streams.begin(), audio_streams.end(),
443*3f982cf4SFabien Sanglard                      [](const AudioStream& a) { return a.IsValid(); }) &&
444*3f982cf4SFabien Sanglard          std::all_of(video_streams.begin(), video_streams.end(),
445*3f982cf4SFabien Sanglard                      [](const VideoStream& v) { return v.IsValid(); });
446*3f982cf4SFabien Sanglard }
447*3f982cf4SFabien Sanglard }  // namespace cast
448*3f982cf4SFabien Sanglard }  // namespace openscreen
449