|
4 | 4 |
|
5 | 5 | #include "cast/streaming/public/receiver_session.h" |
6 | 6 |
|
| 7 | +#include <functional> |
| 8 | +#include <memory> |
7 | 9 | #include <utility> |
8 | 10 |
|
| 11 | +#include "cast/streaming/input.pb.h" |
9 | 12 | #include "cast/streaming/public/receiver.h" |
10 | 13 | #include "cast/streaming/testing/mock_environment.h" |
11 | 14 | #include "cast/streaming/testing/simple_message_port.h" |
|
16 | 19 | #include "platform/test/fake_task_runner.h" |
17 | 20 | #include "util/chrono_helpers.h" |
18 | 21 | #include "util/json/json_serialization.h" |
| 22 | +#include "util/std_util.h" |
19 | 23 |
|
20 | 24 | using ::testing::_; |
21 | 25 | using ::testing::InSequence; |
@@ -314,6 +318,44 @@ constexpr char kRpcMessage[] = R"({ |
314 | 318 | "type" : "RPC" |
315 | 319 | })"; |
316 | 320 |
|
| 321 | +constexpr char kValidOfferMessageWithInput[] = R"({ |
| 322 | + "type": "OFFER", |
| 323 | + "seqNum": 1337, |
| 324 | + "offer": { |
| 325 | + "castMode": "mirroring", |
| 326 | + "supportedStreams": [ |
| 327 | + { |
| 328 | + "index": 31338, |
| 329 | + "type": "video_source", |
| 330 | + "codecName": "vp8", |
| 331 | + "rtpProfile": "cast", |
| 332 | + "rtpPayloadType": 127, |
| 333 | + "ssrc": 19088745, |
| 334 | + "maxFrameRate": "60000/1000", |
| 335 | + "timeBase": "1/90000", |
| 336 | + "maxBitRate": 5000000, |
| 337 | + "profile": "main", |
| 338 | + "level": "4", |
| 339 | + "aesKey": "040d756791711fd3adb939066e6d8690", |
| 340 | + "aesIvMask": "9ff0f022a959150e70a2d05a6c184aed", |
| 341 | + "rtpExtensions": ["input_events"], |
| 342 | + "resolutions": [ |
| 343 | + { |
| 344 | + "width": 1280, |
| 345 | + "height": 720 |
| 346 | + } |
| 347 | + ] |
| 348 | + } |
| 349 | + ] |
| 350 | + } |
| 351 | +})"; |
| 352 | + |
| 353 | +constexpr char kInputMessage[] = R"({ |
| 354 | + "input" : "CGQQnBiCGQgSAggMGgIIBg==", |
| 355 | + "seqNum" : 3, |
| 356 | + "type" : "INPUT" |
| 357 | +})"; |
| 358 | + |
317 | 359 | class FakeClient : public ReceiverSession::Client { |
318 | 360 | public: |
319 | 361 | MOCK_METHOD(void, |
@@ -1068,4 +1110,140 @@ TEST_F(ReceiverSessionTest, EnablesDscpInAnswer) { |
1068 | 1110 | ASSERT_TRUE(dscp2.empty()); |
1069 | 1111 | } |
1070 | 1112 |
|
| 1113 | +TEST_F(ReceiverSessionTest, InputEventsOptIn) { |
| 1114 | + ReceiverConstraints constraints; |
| 1115 | + constraints.supports_input_events = true; |
| 1116 | + SetUpWithConstraints(std::move(constraints)); |
| 1117 | + |
| 1118 | + EXPECT_CALL(client_, OnNegotiated(session_.get(), _)) |
| 1119 | + .WillOnce([](const ReceiverSession* session, |
| 1120 | + ReceiverSession::ConfiguredReceivers cr) { |
| 1121 | + EXPECT_TRUE(cr.input_enabled); |
| 1122 | + }); |
| 1123 | + EXPECT_CALL(client_, |
| 1124 | + OnReceiversDestroying(session_.get(), |
| 1125 | + ReceiverSession::Client::kEndOfSession)); |
| 1126 | + |
| 1127 | + message_port_->ReceiveMessage(kValidOfferMessageWithInput); |
| 1128 | + |
| 1129 | + const std::vector<std::string>& messages = message_port_->posted_messages(); |
| 1130 | + ASSERT_EQ(1u, messages.size()); |
| 1131 | + Json::Value message = ExpectIsValidAnswer(messages[0]); |
| 1132 | + const Json::Value& answer = message["answer"]; |
| 1133 | + |
| 1134 | + bool found_extension = false; |
| 1135 | + for (const auto& ext : answer["rtpExtensions"]) { |
| 1136 | + if (ext.asString() == "input_events") { |
| 1137 | + found_extension = true; |
| 1138 | + break; |
| 1139 | + } |
| 1140 | + } |
| 1141 | + EXPECT_TRUE(found_extension); |
| 1142 | +} |
| 1143 | + |
| 1144 | +TEST_F(ReceiverSessionTest, HandlesInputMessage) { |
| 1145 | + ReceiverConstraints constraints; |
| 1146 | + constraints.supports_input_events = true; |
| 1147 | + SetUpWithConstraints(std::move(constraints)); |
| 1148 | + |
| 1149 | + message_port_->ReceiveMessage(kInputMessage); |
| 1150 | + // Nothing should happen yet, the session doesn't have a messenger. |
| 1151 | + ASSERT_EQ(0u, message_port_->posted_messages().size()); |
| 1152 | + |
| 1153 | + InSequence s; |
| 1154 | + bool received_negotiation = false; |
| 1155 | + EXPECT_CALL(client_, OnNegotiated(session_.get(), _)) |
| 1156 | + .WillOnce([&received_negotiation]( |
| 1157 | + const ReceiverSession* session, |
| 1158 | + ReceiverSession::ConfiguredReceivers receivers) mutable { |
| 1159 | + ASSERT_TRUE(receivers.input_enabled); |
| 1160 | + received_negotiation = true; |
| 1161 | + }); |
| 1162 | + EXPECT_CALL(client_, |
| 1163 | + OnReceiversDestroying(session_.get(), |
| 1164 | + ReceiverSession::Client::kEndOfSession)); |
| 1165 | + |
| 1166 | + message_port_->ReceiveMessage(kValidOfferMessageWithInput); |
| 1167 | + ASSERT_TRUE(received_negotiation); |
| 1168 | +} |
| 1169 | + |
| 1170 | +TEST_F(ReceiverSessionTest, HandlesInputMessengerNotNegotiated) { |
| 1171 | + ReceiverConstraints constraints; |
| 1172 | + constraints.supports_input_events = false; |
| 1173 | + SetUpWithConstraints(std::move(constraints)); |
| 1174 | + |
| 1175 | + EXPECT_CALL(client_, OnNegotiated(session_.get(), _)) |
| 1176 | + .WillOnce([](const ReceiverSession* session, |
| 1177 | + ReceiverSession::ConfiguredReceivers cr) { |
| 1178 | + EXPECT_FALSE(cr.input_enabled); |
| 1179 | + }); |
| 1180 | + EXPECT_CALL(client_, |
| 1181 | + OnReceiversDestroying(session_.get(), |
| 1182 | + ReceiverSession::Client::kEndOfSession)); |
| 1183 | + |
| 1184 | + message_port_->ReceiveMessage(kValidOfferMessageWithInput); |
| 1185 | +} |
| 1186 | + |
| 1187 | +TEST_F(ReceiverSessionTest, HandlesInputMessengerSendsMessage) { |
| 1188 | + ReceiverConstraints constraints; |
| 1189 | + constraints.supports_input_events = true; |
| 1190 | + SetUpWithConstraints(std::move(constraints)); |
| 1191 | + |
| 1192 | + EXPECT_CALL(client_, OnNegotiated(session_.get(), _)) |
| 1193 | + .WillOnce([this](const ReceiverSession* session, |
| 1194 | + ReceiverSession::ConfiguredReceivers cr) { |
| 1195 | + ASSERT_TRUE(cr.input_enabled); |
| 1196 | + |
| 1197 | + InputMessage message; |
| 1198 | + auto* event = message.add_events(); |
| 1199 | + event->set_type(InputMessage::INPUT_TYPE_KEY_DOWN); |
| 1200 | + this->session_->SendInputMessage(message); |
| 1201 | + }); |
| 1202 | + EXPECT_CALL(client_, |
| 1203 | + OnReceiversDestroying(session_.get(), |
| 1204 | + ReceiverSession::Client::kEndOfSession)); |
| 1205 | + |
| 1206 | + message_port_->ReceiveMessage(kValidOfferMessageWithInput); |
| 1207 | + |
| 1208 | + // Verify message was sent through message port. |
| 1209 | + const auto& messages = message_port_->posted_messages(); |
| 1210 | + |
| 1211 | + // We expect at least 2 messages: ANSWER and INPUT. |
| 1212 | + ASSERT_GE(messages.size(), 2u); |
| 1213 | + |
| 1214 | + // Index 0 is ANSWER. |
| 1215 | + ExpectIsValidAnswer(messages[0]); |
| 1216 | + |
| 1217 | + // Index 1 should be INPUT. |
| 1218 | + ErrorOr<Json::Value> input_json = json::Parse(messages[1]); |
| 1219 | + ASSERT_TRUE(input_json.is_value()); |
| 1220 | + EXPECT_EQ("INPUT", input_json.value()["type"].asString()); |
| 1221 | + EXPECT_FALSE(input_json.value()["input"].asString().empty()); |
| 1222 | +} |
| 1223 | + |
| 1224 | +TEST_F(ReceiverSessionTest, HandlesInputMessengerReceivesMessage) { |
| 1225 | + ReceiverConstraints constraints; |
| 1226 | + constraints.supports_input_events = true; |
| 1227 | + SetUpWithConstraints(std::move(constraints)); |
| 1228 | + |
| 1229 | + bool received_input = false; |
| 1230 | + session_->SetInputCallback( |
| 1231 | + [&received_input](InputMessage message) { received_input = true; }); |
| 1232 | + |
| 1233 | + EXPECT_CALL(client_, OnNegotiated(session_.get(), _)) |
| 1234 | + .WillOnce([](const ReceiverSession* session, |
| 1235 | + ReceiverSession::ConfiguredReceivers cr) { |
| 1236 | + ASSERT_TRUE(cr.input_enabled); |
| 1237 | + }); |
| 1238 | + |
| 1239 | + message_port_->ReceiveMessage(kValidOfferMessageWithInput); |
| 1240 | + |
| 1241 | + message_port_->ReceiveMessage(kInputMessage); |
| 1242 | + ASSERT_TRUE(received_input); |
| 1243 | + |
| 1244 | + EXPECT_CALL(client_, |
| 1245 | + OnReceiversDestroying(session_.get(), |
| 1246 | + ReceiverSession::Client::kEndOfSession)); |
| 1247 | +} |
| 1248 | + |
1071 | 1249 | } // namespace openscreen::cast |
0 commit comments