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