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