xref: /aosp_15_r20/external/openscreen/cast/streaming/offer_messages_unittest.cc (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "cast/streaming/offer_messages.h"
6 
7 #include <limits>
8 #include <utility>
9 
10 #include "cast/streaming/rtp_defines.h"
11 #include "gmock/gmock.h"
12 #include "gtest/gtest.h"
13 #include "util/json/json_serialization.h"
14 
15 using ::testing::ElementsAre;
16 
17 namespace openscreen {
18 namespace cast {
19 
20 namespace {
21 
22 constexpr char kValidOffer[] = R"({
23   "castMode": "mirroring",
24   "supportedStreams": [
25     {
26       "index": 0,
27       "type": "video_source",
28       "codecName": "h264",
29       "rtpProfile": "cast",
30       "rtpPayloadType": 101,
31       "ssrc": 19088743,
32       "maxFrameRate": "60000/1000",
33       "timeBase": "1/90000",
34       "maxBitRate": 5000000,
35       "profile": "main",
36       "level": "4",
37       "targetDelay": 200,
38       "aesKey": "040d756791711fd3adb939066e6d8690",
39       "aesIvMask": "9ff0f022a959150e70a2d05a6c184aed",
40       "resolutions": [
41         {
42           "width": 1280,
43           "height": 720
44         },
45         {
46           "width": 640,
47           "height": 360
48         },
49         {
50           "width": 640,
51           "height": 480
52         }
53       ]
54     },
55     {
56       "index": 1,
57       "type": "video_source",
58       "codecName": "vp8",
59       "rtpProfile": "cast",
60       "rtpPayloadType": 100,
61       "ssrc": 19088744,
62       "maxFrameRate": "30000/1001",
63       "targetDelay": 1000,
64       "timeBase": "1/90000",
65       "maxBitRate": 5000000,
66       "profile": "main",
67       "level": "5",
68       "aesKey": "bbf109bf84513b456b13a184453b66ce",
69       "aesIvMask": "edaf9e4536e2b66191f560d9c04b2a69"
70     },
71     {
72       "index": 2,
73       "type": "audio_source",
74       "codecName": "opus",
75       "targetDelay": 300,
76       "rtpProfile": "cast",
77       "rtpPayloadType": 96,
78       "ssrc": 4294967295,
79       "bitRate": 124000,
80       "timeBase": "1/48000",
81       "channels": 2,
82       "aesKey": "51027e4e2347cbcb49d57ef10177aebc",
83       "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1"
84     },
85     {
86       "index": 3,
87       "type": "video_source",
88       "codecName": "av1",
89       "rtpProfile": "cast",
90       "rtpPayloadType": 104,
91       "ssrc": 19088744,
92       "maxFrameRate": "30000/1001",
93       "targetDelay": 1000,
94       "timeBase": "1/90000",
95       "maxBitRate": 5000000,
96       "profile": "main",
97       "level": "5",
98       "aesKey": "bbf109bf84513b456b13a184453b66ce",
99       "aesIvMask": "edaf9e4536e2b66191f560d9c04b2a69"
100     }
101   ]
102 })";
103 
ExpectFailureOnParse(absl::string_view body,absl::optional<Error::Code> expected=absl::nullopt)104 void ExpectFailureOnParse(
105     absl::string_view body,
106     absl::optional<Error::Code> expected = absl::nullopt) {
107   ErrorOr<Json::Value> root = json::Parse(body);
108   ASSERT_TRUE(root.is_value()) << root.error();
109 
110   Offer offer;
111   Error error = Offer::TryParse(std::move(root.value()), &offer);
112   EXPECT_FALSE(error.ok());
113   if (expected) {
114     EXPECT_EQ(expected, error.code());
115   }
116 }
117 
ExpectEqualsValidOffer(const Offer & offer)118 void ExpectEqualsValidOffer(const Offer& offer) {
119   EXPECT_EQ(CastMode::kMirroring, offer.cast_mode);
120 
121   // Verify list of video streams.
122   EXPECT_EQ(3u, offer.video_streams.size());
123   const auto& video_streams = offer.video_streams;
124 
125   const bool flipped = video_streams[0].stream.index != 0;
126   const VideoStream& vs_one = flipped ? video_streams[2] : video_streams[0];
127   const VideoStream& vs_two = video_streams[1];
128   const VideoStream& vs_three = flipped ? video_streams[0] : video_streams[2];
129 
130   EXPECT_EQ(0, vs_one.stream.index);
131   EXPECT_EQ(1, vs_one.stream.channels);
132   EXPECT_EQ(Stream::Type::kVideoSource, vs_one.stream.type);
133   EXPECT_EQ(VideoCodec::kH264, vs_one.codec);
134   EXPECT_EQ(RtpPayloadType::kVideoH264, vs_one.stream.rtp_payload_type);
135   EXPECT_EQ(19088743u, vs_one.stream.ssrc);
136   EXPECT_EQ((SimpleFraction{60000, 1000}), vs_one.max_frame_rate);
137   EXPECT_EQ(90000, vs_one.stream.rtp_timebase);
138   EXPECT_EQ(5000000, vs_one.max_bit_rate);
139   EXPECT_EQ("main", vs_one.profile);
140   EXPECT_EQ("4", vs_one.level);
141   EXPECT_THAT(vs_one.stream.aes_key,
142               ElementsAre(0x04, 0x0d, 0x75, 0x67, 0x91, 0x71, 0x1f, 0xd3, 0xad,
143                           0xb9, 0x39, 0x06, 0x6e, 0x6d, 0x86, 0x90));
144   EXPECT_THAT(vs_one.stream.aes_iv_mask,
145               ElementsAre(0x9f, 0xf0, 0xf0, 0x22, 0xa9, 0x59, 0x15, 0x0e, 0x70,
146                           0xa2, 0xd0, 0x5a, 0x6c, 0x18, 0x4a, 0xed));
147 
148   const auto& resolutions = vs_one.resolutions;
149   EXPECT_EQ(3u, resolutions.size());
150   const Resolution& r_one = resolutions[0];
151   EXPECT_EQ(1280, r_one.width);
152   EXPECT_EQ(720, r_one.height);
153 
154   const Resolution& r_two = resolutions[1];
155   EXPECT_EQ(640, r_two.width);
156   EXPECT_EQ(360, r_two.height);
157 
158   const Resolution& r_three = resolutions[2];
159   EXPECT_EQ(640, r_three.width);
160   EXPECT_EQ(480, r_three.height);
161 
162   EXPECT_EQ(1, vs_two.stream.index);
163   EXPECT_EQ(1, vs_two.stream.channels);
164   EXPECT_EQ(Stream::Type::kVideoSource, vs_two.stream.type);
165   EXPECT_EQ(VideoCodec::kVp8, vs_two.codec);
166   EXPECT_EQ(RtpPayloadType::kVideoVp8, vs_two.stream.rtp_payload_type);
167   EXPECT_EQ(19088744u, vs_two.stream.ssrc);
168   EXPECT_EQ((SimpleFraction{30000, 1001}), vs_two.max_frame_rate);
169   EXPECT_EQ(90000, vs_two.stream.rtp_timebase);
170   EXPECT_EQ(5000000, vs_two.max_bit_rate);
171   EXPECT_EQ("main", vs_two.profile);
172   EXPECT_EQ("5", vs_two.level);
173   EXPECT_THAT(vs_two.stream.aes_key,
174               ElementsAre(0xbb, 0xf1, 0x09, 0xbf, 0x84, 0x51, 0x3b, 0x45, 0x6b,
175                           0x13, 0xa1, 0x84, 0x45, 0x3b, 0x66, 0xce));
176   EXPECT_THAT(vs_two.stream.aes_iv_mask,
177               ElementsAre(0xed, 0xaf, 0x9e, 0x45, 0x36, 0xe2, 0xb6, 0x61, 0x91,
178                           0xf5, 0x60, 0xd9, 0xc0, 0x4b, 0x2a, 0x69));
179 
180   const auto& resolutions_two = vs_two.resolutions;
181   EXPECT_EQ(0u, resolutions_two.size());
182 
183   EXPECT_EQ(3, vs_three.stream.index);
184   EXPECT_EQ(1, vs_three.stream.channels);
185   EXPECT_EQ(Stream::Type::kVideoSource, vs_three.stream.type);
186   EXPECT_EQ(VideoCodec::kAv1, vs_three.codec);
187   EXPECT_EQ(RtpPayloadType::kVideoAv1, vs_three.stream.rtp_payload_type);
188   EXPECT_EQ(19088744u, vs_three.stream.ssrc);
189   EXPECT_EQ((SimpleFraction{30000, 1001}), vs_three.max_frame_rate);
190   EXPECT_EQ(90000, vs_three.stream.rtp_timebase);
191   EXPECT_EQ(5000000, vs_three.max_bit_rate);
192   EXPECT_EQ("main", vs_three.profile);
193   EXPECT_EQ("5", vs_three.level);
194   EXPECT_THAT(vs_three.stream.aes_key,
195               ElementsAre(0xbb, 0xf1, 0x09, 0xbf, 0x84, 0x51, 0x3b, 0x45, 0x6b,
196                           0x13, 0xa1, 0x84, 0x45, 0x3b, 0x66, 0xce));
197   EXPECT_THAT(vs_three.stream.aes_iv_mask,
198               ElementsAre(0xed, 0xaf, 0x9e, 0x45, 0x36, 0xe2, 0xb6, 0x61, 0x91,
199                           0xf5, 0x60, 0xd9, 0xc0, 0x4b, 0x2a, 0x69));
200 
201   const auto& resolutions_three = vs_three.resolutions;
202   EXPECT_EQ(0u, resolutions_three.size());
203 
204   // Verify list of audio streams.
205   EXPECT_EQ(1u, offer.audio_streams.size());
206   const AudioStream& as = offer.audio_streams[0];
207   EXPECT_EQ(2, as.stream.index);
208   EXPECT_EQ(Stream::Type::kAudioSource, as.stream.type);
209   EXPECT_EQ(AudioCodec::kOpus, as.codec);
210   EXPECT_EQ(RtpPayloadType::kAudioOpus, as.stream.rtp_payload_type);
211   EXPECT_EQ(std::numeric_limits<Ssrc>::max(), as.stream.ssrc);
212   EXPECT_EQ(124000, as.bit_rate);
213   EXPECT_EQ(2, as.stream.channels);
214 
215   EXPECT_THAT(as.stream.aes_key,
216               ElementsAre(0x51, 0x02, 0x7e, 0x4e, 0x23, 0x47, 0xcb, 0xcb, 0x49,
217                           0xd5, 0x7e, 0xf1, 0x01, 0x77, 0xae, 0xbc));
218   EXPECT_THAT(as.stream.aes_iv_mask,
219               ElementsAre(0x7f, 0x12, 0xa1, 0x9b, 0xe6, 0x2a, 0x36, 0xc0, 0x4a,
220                           0xe4, 0x11, 0x6c, 0xaa, 0xef, 0xf6, 0xd1));
221 }
222 
223 }  // namespace
224 
TEST(OfferTest,ErrorOnEmptyOffer)225 TEST(OfferTest, ErrorOnEmptyOffer) {
226   ExpectFailureOnParse("{}");
227 }
228 
TEST(OfferTest,ErrorOnMissingMandatoryFields)229 TEST(OfferTest, ErrorOnMissingMandatoryFields) {
230   // It's okay if castMode is omitted, but if supportedStreams is omitted we
231   // should fail here.
232   ExpectFailureOnParse(R"({
233     "castMode": "mirroring"
234   })");
235 }
236 
TEST(OfferTest,CanParseValidButStreamlessOffer)237 TEST(OfferTest, CanParseValidButStreamlessOffer) {
238   ErrorOr<Json::Value> root = json::Parse(R"({
239     "castMode": "mirroring",
240     "supportedStreams": []
241   })");
242   ASSERT_TRUE(root.is_value()) << root.error();
243 
244   Offer offer;
245   EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &offer).ok());
246 }
247 
TEST(OfferTest,ErrorOnMissingAudioStreamMandatoryField)248 TEST(OfferTest, ErrorOnMissingAudioStreamMandatoryField) {
249   ExpectFailureOnParse(R"({
250     "castMode": "mirroring",
251     "supportedStreams": [{
252       "index": 2,
253       "codecName": "opus",
254       "rtpProfile": "cast",
255       "rtpPayloadType": 96,
256       "ssrc": 19088743,
257       "bitRate": 124000,
258       "timeBase": "1/48000",
259       "channels": 2
260     }]})");
261 
262   ExpectFailureOnParse(R"({
263     "castMode": "mirroring",
264     "supportedStreams": [{
265       "index": 2,
266       "type": "audio_source",
267       "codecName": "opus",
268       "rtpProfile": "cast",
269       "rtpPayloadType": 96,
270       "bitRate": 124000,
271       "timeBase": "1/48000",
272       "channels": 2
273     }]})");
274 }
275 
TEST(OfferTest,CanParseValidButMinimalAudioOffer)276 TEST(OfferTest, CanParseValidButMinimalAudioOffer) {
277   ErrorOr<Json::Value> root = json::Parse(R"({
278     "castMode": "mirroring",
279     "supportedStreams": [{
280       "index": 2,
281       "type": "audio_source",
282       "codecName": "opus",
283       "rtpProfile": "cast",
284       "rtpPayloadType": 96,
285       "ssrc": 19088743,
286       "bitRate": 124000,
287       "timeBase": "1/48000",
288       "channels": 2,
289       "aesKey": "51027e4e2347cbcb49d57ef10177aebc",
290       "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1"
291     }]
292   })");
293   ASSERT_TRUE(root.is_value());
294   Offer offer;
295   EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &offer).ok());
296 }
297 
TEST(OfferTest,CanParseValidZeroBitRateAudioOffer)298 TEST(OfferTest, CanParseValidZeroBitRateAudioOffer) {
299   ErrorOr<Json::Value> root = json::Parse(R"({
300     "castMode": "mirroring",
301     "supportedStreams": [{
302       "index": 2,
303       "type": "audio_source",
304       "codecName": "opus",
305       "rtpProfile": "cast",
306       "rtpPayloadType": 96,
307       "ssrc": 19088743,
308       "bitRate": 0,
309       "timeBase": "1/48000",
310       "channels": 5,
311       "aesKey": "51029e4e2347cbcb49d57ef10177aebd",
312       "aesIvMask": "7f12a19be62a36c04ae4116caaeff5d2"
313     }]
314   })");
315   ASSERT_TRUE(root.is_value()) << root.error();
316   Offer offer;
317   EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &offer).ok());
318 }
319 
TEST(OfferTest,ErrorOnInvalidRtpTimebase)320 TEST(OfferTest, ErrorOnInvalidRtpTimebase) {
321   ExpectFailureOnParse(R"({
322     "castMode": "mirroring",
323     "supportedStreams": [{
324       "index": 2,
325       "type": "audio_source",
326       "codecName": "opus",
327       "rtpProfile": "cast",
328       "rtpPayloadType": 96,
329       "ssrc": 19088743,
330       "bitRate": 124000,
331       "timeBase": "1/10000000",
332       "channels": 2,
333       "aesKey": "51027e4e2347cbcb49d57ef10177aebc",
334       "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1"
335     }]
336   })");
337 
338   ExpectFailureOnParse(R"({
339     "castMode": "mirroring",
340     "supportedStreams": [{
341       "index": 2,
342       "type": "audio_source",
343       "codecName": "opus",
344       "rtpProfile": "cast",
345       "rtpPayloadType": 96,
346       "ssrc": 19088743,
347       "bitRate": 124000,
348       "timeBase": "0",
349       "channels": 2,
350       "aesKey": "51027e4e2347cbcb49d57ef10177aebc",
351       "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1"
352     }]
353   })");
354 
355   ExpectFailureOnParse(R"({
356     "castMode": "mirroring",
357     "supportedStreams": [{
358       "index": 2,
359       "type": "audio_source",
360       "codecName": "opus",
361       "rtpProfile": "cast",
362       "rtpPayloadType": 96,
363       "ssrc": 19088743,
364       "bitRate": 124000,
365       "timeBase": "1/1",
366       "channels": 2,
367       "aesKey": "51027e4e2347cbcb49d57ef10177aebc",
368       "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1"
369     }]
370   })");
371 
372   ExpectFailureOnParse(R"({
373     "castMode": "mirroring",
374     "supportedStreams": [{
375       "index": 2,
376       "type": "audio_source",
377       "codecName": "opus",
378       "rtpProfile": "cast",
379       "rtpPayloadType": 96,
380       "ssrc": 19088743,
381       "bitRate": 124000,
382       "timeBase": "really fast plz, kthx",
383       "channels": 2,
384       "aesKey": "51027e4e2347cbcb49d57ef10177aebc",
385       "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1"
386     }]
387   })");
388 }
389 
TEST(OfferTest,ErrorOnMissingVideoStreamMandatoryField)390 TEST(OfferTest, ErrorOnMissingVideoStreamMandatoryField) {
391   ExpectFailureOnParse(R"({
392     "castMode": "mirroring",
393     "supportedStreams": [{
394       "index": 2,
395       "codecName": "video_source",
396       "rtpProfile": "h264",
397       "rtpPayloadType": 101,
398       "ssrc": 19088743,
399       "bitRate": 124000,
400       "timeBase": "1/48000"
401     }]
402   })");
403 
404   ExpectFailureOnParse(R"({
405     "castMode": "mirroring",
406     "supportedStreams": [{
407       "index": 2,
408       "type": "video_source",
409       "codecName": "h264",
410       "rtpProfile": "cast",
411       "rtpPayloadType": 101,
412       "bitRate": 124000,
413       "timeBase": "1/48000",
414        "maxBitRate": 10000
415     }]
416   })");
417 
418   ExpectFailureOnParse(R"({
419     "castMode": "mirroring",
420     "supportedStreams": [{
421       "index": 2,
422       "type": "video_source",
423       "codecName": "vp8",
424       "rtpProfile": "cast",
425       "rtpPayloadType": 100,
426       "ssrc": 19088743,
427       "timeBase": "1/48000",
428        "resolutions": [],
429        "maxBitRate": 10000
430     }]
431   })");
432 
433   ExpectFailureOnParse(R"({
434     "castMode": "mirroring",
435     "supportedStreams": [{
436       "index": 2,
437       "type": "video_source",
438       "codecName": "vp8",
439       "rtpProfile": "cast",
440       "rtpPayloadType": 100,
441       "ssrc": 19088743,
442       "timeBase": "1/48000",
443        "resolutions": [],
444        "maxBitRate": 10000,
445        "aesKey": "51027e4e2347cbcb49d57ef10177aebc"
446     }]
447   })");
448 
449   ExpectFailureOnParse(R"({
450     "castMode": "mirroring",
451     "supportedStreams": [{
452       "index": 2,
453       "type": "video_source",
454       "codecName": "vp8",
455       "rtpProfile": "cast",
456       "rtpPayloadType": 100,
457       "ssrc": 19088743,
458       "timeBase": "1/48000",
459        "resolutions": [],
460        "maxBitRate": 10000,
461        "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1"
462     }]
463   })");
464 }
465 
TEST(OfferTest,ValidatesCodecParameterFormat)466 TEST(OfferTest, ValidatesCodecParameterFormat) {
467   ExpectFailureOnParse(R"({
468     "castMode": "mirroring",
469     "supportedStreams": [{
470       "index": 2,
471       "type": "audio_source",
472       "codecName": "aac",
473       "codecParameter": "vp08.123.332",
474       "rtpProfile": "cast",
475       "rtpPayloadType": 96,
476       "ssrc": 19088743,
477       "bitRate": 124000,
478       "timeBase": "1/10000000",
479       "channels": 2,
480       "aesKey": "51027e4e2347cbcb49d57ef10177aebc",
481       "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1"
482     }]
483   })");
484 
485   ExpectFailureOnParse(R"({
486     "castMode": "mirroring",
487     "supportedStreams": [{
488       "index": 2,
489       "type": "video_source",
490       "codecName": "vp8",
491       "codecParameter": "vp09.11.23",
492       "rtpProfile": "cast",
493       "rtpPayloadType": 100,
494       "ssrc": 19088743,
495       "timeBase": "1/48000",
496        "resolutions": [],
497        "maxBitRate": 10000,
498        "aesKey": "51027e4e2347cbcb49d57ef10177aebc"
499     }]
500   })");
501 
502   const ErrorOr<Json::Value> audio_root = json::Parse(R"({
503     "castMode": "mirroring",
504     "supportedStreams": [{
505       "index": 2,
506       "type": "audio_source",
507       "codecName": "aac",
508       "codecParameter": "mp4a.12",
509       "rtpProfile": "cast",
510       "rtpPayloadType": 96,
511       "ssrc": 19088743,
512       "bitRate": 124000,
513       "timeBase": "1/10000000",
514       "channels": 2,
515       "aesKey": "51027e4e2347cbcb49d57ef10177aebc",
516       "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1"
517     }]
518   })");
519   ASSERT_TRUE(audio_root.is_value()) << audio_root.error();
520 
521   const ErrorOr<Json::Value> video_root = json::Parse(R"({
522     "castMode": "mirroring",
523     "supportedStreams": [{
524       "index": 2,
525       "type": "video_source",
526       "codecName": "vp9",
527       "codecParameter": "vp09.11.23",
528       "rtpProfile": "cast",
529       "rtpPayloadType": 100,
530       "ssrc": 19088743,
531       "timeBase": "1/48000",
532        "resolutions": [],
533        "maxBitRate": 10000,
534        "aesKey": "51027e4e2347cbcb49d57ef10177aebc"
535     }]
536   })");
537   ASSERT_TRUE(video_root.is_value()) << video_root.error();
538 }
539 
TEST(OfferTest,CanParseValidButMinimalVideoOffer)540 TEST(OfferTest, CanParseValidButMinimalVideoOffer) {
541   ErrorOr<Json::Value> root = json::Parse(R"({
542     "castMode": "mirroring",
543     "supportedStreams": [{
544       "index": 2,
545       "type": "video_source",
546       "codecName": "vp8",
547       "rtpProfile": "cast",
548       "rtpPayloadType": 100,
549       "ssrc": 19088743,
550       "timeBase": "1/48000",
551        "resolutions": [],
552        "maxBitRate": 10000,
553        "aesKey": "51027e4e2347cbcb49d57ef10177aebc",
554        "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1"
555     }]
556   })");
557 
558   ASSERT_TRUE(root.is_value());
559   Offer offer;
560   EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &offer).ok());
561 }
562 
TEST(OfferTest,CanParseValidOffer)563 TEST(OfferTest, CanParseValidOffer) {
564   ErrorOr<Json::Value> root = json::Parse(kValidOffer);
565   ASSERT_TRUE(root.is_value());
566   Offer offer;
567   EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &offer).ok());
568 
569   ExpectEqualsValidOffer(offer);
570 }
571 
TEST(OfferTest,ParseAndToJsonResultsInSameOffer)572 TEST(OfferTest, ParseAndToJsonResultsInSameOffer) {
573   ErrorOr<Json::Value> root = json::Parse(kValidOffer);
574   ASSERT_TRUE(root.is_value());
575   Offer offer;
576   EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &offer).ok());
577   ExpectEqualsValidOffer(offer);
578 
579   Offer reparsed_offer;
580   EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &reparsed_offer).ok());
581   ExpectEqualsValidOffer(reparsed_offer);
582 }
583 
584 // We don't want to enforce that a given offer must have both audio and
585 // video, so we don't assert on either.
TEST(OfferTest,IsValidWithMissingStreams)586 TEST(OfferTest, IsValidWithMissingStreams) {
587   ErrorOr<Json::Value> root = json::Parse(kValidOffer);
588   ASSERT_TRUE(root.is_value());
589   Offer offer;
590   EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &offer).ok());
591   ExpectEqualsValidOffer(offer);
592   const Offer valid_offer = std::move(offer);
593 
594   Offer missing_audio_streams = valid_offer;
595   missing_audio_streams.audio_streams.clear();
596   EXPECT_TRUE(missing_audio_streams.IsValid());
597 
598   Offer missing_video_streams = valid_offer;
599   missing_video_streams.audio_streams.clear();
600   EXPECT_TRUE(missing_video_streams.IsValid());
601 }
602 
TEST(OfferTest,InvalidIfInvalidStreams)603 TEST(OfferTest, InvalidIfInvalidStreams) {
604   ErrorOr<Json::Value> root = json::Parse(kValidOffer);
605   ASSERT_TRUE(root.is_value());
606   Offer offer;
607   EXPECT_TRUE(Offer::TryParse(std::move(root.value()), &offer).ok());
608   ExpectEqualsValidOffer(offer);
609 
610   Offer video_stream_invalid = offer;
611   video_stream_invalid.video_streams[0].max_frame_rate = SimpleFraction{1, 0};
612   EXPECT_FALSE(video_stream_invalid.IsValid());
613 
614   Offer audio_stream_invalid = offer;
615   video_stream_invalid.audio_streams[0].bit_rate = 0;
616   EXPECT_FALSE(video_stream_invalid.IsValid());
617 }
618 
TEST(OfferTest,FailsIfUnencrypted)619 TEST(OfferTest, FailsIfUnencrypted) {
620   // Video stream missing AES fields.
621   ExpectFailureOnParse(R"({
622     "castMode": "mirroring",
623     "supportedStreams": [{
624       "index": 2,
625       "type": "video_source",
626       "codecName": "vp8",
627       "rtpProfile": "cast",
628       "rtpPayloadType": 100,
629       "ssrc": 19088743,
630       "timeBase": "1/48000",
631        "resolutions": [],
632        "maxBitRate": 10000,
633        "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1"
634     }]
635   })",
636                        Error::Code::kUnencryptedOffer);
637 
638   ExpectFailureOnParse(R"({
639     "castMode": "mirroring",
640     "supportedStreams": [{
641       "index": 2,
642       "type": "video_source",
643       "codecName": "vp8",
644       "rtpProfile": "cast",
645       "rtpPayloadType": 100,
646       "ssrc": 19088743,
647       "timeBase": "1/48000",
648        "resolutions": [],
649        "maxBitRate": 10000,
650        "aesKey": "51027e4e2347cbcb49d57ef10177aebc"
651     }]
652   })",
653                        Error::Code::kUnencryptedOffer);
654 
655   // Audio stream missing AES fields.
656   ExpectFailureOnParse(R"({
657     "castMode": "mirroring",
658     "supportedStreams": [{
659       "index": 2,
660       "type": "audio_source",
661       "codecName": "opus",
662       "rtpProfile": "cast",
663       "rtpPayloadType": 96,
664       "ssrc": 19088743,
665       "bitRate": 124000,
666       "timeBase": "1/48000",
667       "channels": 2,
668       "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1"
669     }]
670   })",
671                        Error::Code::kUnencryptedOffer);
672 
673   ExpectFailureOnParse(R"({
674     "castMode": "mirroring",
675     "supportedStreams": [{
676       "index": 2,
677       "type": "audio_source",
678       "codecName": "opus",
679       "rtpProfile": "cast",
680       "rtpPayloadType": 96,
681       "ssrc": 19088743,
682       "bitRate": 124000,
683       "timeBase": "1/48000",
684       "channels": 2,
685       "aesKey": "51027e4e2347cbcb49d57ef10177aebc"
686     }]
687   })",
688                        Error::Code::kUnencryptedOffer);
689 
690   // And finally, fields provided but not properly formatted.
691   ExpectFailureOnParse(R"({
692     "castMode": "mirroring",
693     "supportedStreams": [{
694       "index": 2,
695       "type": "audio_source",
696       "codecName": "opus",
697       "rtpProfile": "cast",
698       "rtpPayloadType": 96,
699       "ssrc": 19088743,
700       "bitRate": 124000,
701       "timeBase": "1/48000",
702       "channels": 2,
703       "aesKey": "51027e4e2347$bcb49d57ef10177aebc",
704       "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1"
705     }]
706   })",
707                        Error::Code::kUnencryptedOffer);
708 }
709 
710 }  // namespace cast
711 }  // namespace openscreen
712