| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/renderer/media/media_stream_dispatcher.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/memory/ref_counted.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "content/common/media/media_stream_messages.h" |
| #include "content/public/common/media_stream_request.h" |
| #include "content/renderer/media/media_stream_dispatcher_eventhandler.h" |
| #include "media/base/audio_parameters.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| namespace content { |
| namespace { |
| |
| const int kRouteId = 0; |
| const int kAudioSessionId = 3; |
| const int kVideoSessionId = 5; |
| const int kScreenSessionId = 7; |
| const int kRequestId1 = 10; |
| const int kRequestId2 = 20; |
| |
| const MediaStreamType kAudioType = MEDIA_DEVICE_AUDIO_CAPTURE; |
| const MediaStreamType kVideoType = MEDIA_DEVICE_VIDEO_CAPTURE; |
| const MediaStreamType kScreenType = MEDIA_DESKTOP_VIDEO_CAPTURE; |
| |
| class MockMediaStreamDispatcherEventHandler |
| : public MediaStreamDispatcherEventHandler, |
| public base::SupportsWeakPtr<MockMediaStreamDispatcherEventHandler> { |
| public: |
| MockMediaStreamDispatcherEventHandler() : request_id_(-1) {} |
| |
| void OnStreamGenerated( |
| int request_id, |
| const std::string& label, |
| const StreamDeviceInfoArray& audio_device_array, |
| const StreamDeviceInfoArray& video_device_array) override { |
| request_id_ = request_id; |
| label_ = label; |
| if (audio_device_array.size()) { |
| DCHECK(audio_device_array.size() == 1); |
| audio_device_ = audio_device_array[0]; |
| } |
| if (video_device_array.size()) { |
| DCHECK(video_device_array.size() == 1); |
| video_device_ = video_device_array[0]; |
| } |
| } |
| |
| void OnStreamGenerationFailed( |
| int request_id, |
| content::MediaStreamRequestResult result) override { |
| request_id_ = request_id; |
| } |
| |
| void OnDeviceStopped(const std::string& label, |
| const StreamDeviceInfo& device_info) override { |
| device_stopped_label_ = label; |
| if (IsVideoMediaType(device_info.device.type)) { |
| EXPECT_TRUE(StreamDeviceInfo::IsEqual(video_device_, device_info)); |
| } |
| if (IsAudioInputMediaType(device_info.device.type)) { |
| EXPECT_TRUE(StreamDeviceInfo::IsEqual(audio_device_, device_info)); |
| } |
| } |
| |
| void OnDeviceOpened(int request_id, |
| const std::string& label, |
| const StreamDeviceInfo& video_device) override { |
| request_id_ = request_id; |
| label_ = label; |
| } |
| |
| void OnDeviceOpenFailed(int request_id) override { request_id_ = request_id; } |
| |
| void ResetStoredParameters() { |
| request_id_ = -1; |
| label_ = ""; |
| device_stopped_label_ = ""; |
| audio_device_ = StreamDeviceInfo(); |
| video_device_ = StreamDeviceInfo(); |
| } |
| |
| int request_id_; |
| std::string label_; |
| std::string device_stopped_label_; |
| StreamDeviceInfo audio_device_; |
| StreamDeviceInfo video_device_; |
| }; |
| |
| class MediaStreamDispatcherUnderTest : public MediaStreamDispatcher { |
| public: |
| MediaStreamDispatcherUnderTest() : MediaStreamDispatcher(NULL) {} |
| |
| using MediaStreamDispatcher::GetNextIpcIdForTest; |
| using RenderFrameObserver::OnMessageReceived; |
| }; |
| |
| class MediaStreamDispatcherTest : public ::testing::Test { |
| public: |
| MediaStreamDispatcherTest() |
| : dispatcher_(new MediaStreamDispatcherUnderTest()), |
| handler_(new MockMediaStreamDispatcherEventHandler), |
| security_origin_(GURL("http://test.com")) {} |
| |
| // Generates a request for a MediaStream and returns the request id that is |
| // used in IPC. Use this returned id in CompleteGenerateStream to identify |
| // the request. |
| int GenerateStream(const StreamControls& controls, int request_id) { |
| int next_ipc_id = dispatcher_->GetNextIpcIdForTest(); |
| dispatcher_->GenerateStream(request_id, handler_.get()->AsWeakPtr(), |
| controls, security_origin_, true); |
| return next_ipc_id; |
| } |
| |
| // CompleteGenerateStream create a MediaStreamMsg_StreamGenerated instance |
| // and call the MediaStreamDispathcer::OnMessageReceived. |ipc_id| must be the |
| // the id returned by GenerateStream. |
| std::string CompleteGenerateStream(int ipc_id, |
| const StreamControls& controls, |
| int request_id) { |
| StreamDeviceInfoArray audio_device_array(controls.audio.requested ? 1 : 0); |
| if (controls.audio.requested) { |
| StreamDeviceInfo audio_device_info; |
| audio_device_info.device.name = "Microphone"; |
| audio_device_info.device.type = kAudioType; |
| audio_device_info.session_id = kAudioSessionId; |
| audio_device_array[0] = audio_device_info; |
| } |
| |
| StreamDeviceInfoArray video_device_array(controls.video.requested ? 1 : 0); |
| if (controls.video.requested) { |
| StreamDeviceInfo video_device_info; |
| video_device_info.device.name = "Camera"; |
| video_device_info.device.type = kVideoType; |
| video_device_info.session_id = kVideoSessionId; |
| video_device_array[0] = video_device_info; |
| } |
| |
| std::string label = "stream" + base::IntToString(ipc_id); |
| |
| handler_->ResetStoredParameters(); |
| dispatcher_->OnMessageReceived(MediaStreamMsg_StreamGenerated( |
| kRouteId, ipc_id, label, |
| audio_device_array, video_device_array)); |
| |
| EXPECT_EQ(handler_->request_id_, request_id); |
| EXPECT_EQ(handler_->label_, label); |
| |
| if (controls.audio.requested) |
| EXPECT_EQ(dispatcher_->audio_session_id(label, 0), kAudioSessionId); |
| |
| if (controls.video.requested) |
| EXPECT_EQ(dispatcher_->video_session_id(label, 0), kVideoSessionId); |
| |
| return label; |
| } |
| |
| protected: |
| base::MessageLoop message_loop_; |
| std::unique_ptr<MediaStreamDispatcherUnderTest> dispatcher_; |
| std::unique_ptr<MockMediaStreamDispatcherEventHandler> handler_; |
| url::Origin security_origin_; |
| }; |
| |
| } // namespace |
| |
| TEST_F(MediaStreamDispatcherTest, GenerateStreamAndStopDevices) { |
| StreamControls controls(true, true); |
| |
| int ipc_request_id1 = GenerateStream(controls, kRequestId1); |
| int ipc_request_id2 = GenerateStream(controls, kRequestId2); |
| EXPECT_NE(ipc_request_id1, ipc_request_id2); |
| |
| // Complete the creation of stream1. |
| const std::string& label1 = |
| CompleteGenerateStream(ipc_request_id1, controls, kRequestId1); |
| |
| // Complete the creation of stream2. |
| const std::string& label2 = |
| CompleteGenerateStream(ipc_request_id2, controls, kRequestId2); |
| |
| // Stop the actual audio device and verify that there is no valid |
| // |session_id|. |
| dispatcher_->StopStreamDevice(handler_->audio_device_); |
| EXPECT_EQ(dispatcher_->audio_session_id(label1, 0), |
| StreamDeviceInfo::kNoId); |
| EXPECT_EQ(dispatcher_->audio_session_id(label2, 0), |
| StreamDeviceInfo::kNoId); |
| |
| // Stop the actual video device and verify that there is no valid |
| // |session_id|. |
| dispatcher_->StopStreamDevice(handler_->video_device_); |
| EXPECT_EQ(dispatcher_->video_session_id(label1, 0), |
| StreamDeviceInfo::kNoId); |
| EXPECT_EQ(dispatcher_->video_session_id(label2, 0), |
| StreamDeviceInfo::kNoId); |
| } |
| |
| TEST_F(MediaStreamDispatcherTest, BasicVideoDevice) { |
| std::unique_ptr<MediaStreamDispatcher> dispatcher( |
| new MediaStreamDispatcher(NULL)); |
| std::unique_ptr<MockMediaStreamDispatcherEventHandler> handler1( |
| new MockMediaStreamDispatcherEventHandler); |
| std::unique_ptr<MockMediaStreamDispatcherEventHandler> handler2( |
| new MockMediaStreamDispatcherEventHandler); |
| url::Origin security_origin; |
| |
| StreamDeviceInfoArray video_device_array(1); |
| StreamDeviceInfo video_device_info; |
| video_device_info.device.name = "Camera"; |
| video_device_info.device.id = "device_path"; |
| video_device_info.device.type = kVideoType; |
| video_device_info.session_id = kVideoSessionId; |
| video_device_array[0] = video_device_info; |
| |
| EXPECT_EQ(dispatcher->requests_.size(), size_t(0)); |
| EXPECT_EQ(dispatcher->label_stream_map_.size(), size_t(0)); |
| |
| int ipc_request_id1 = dispatcher->next_ipc_id_; |
| dispatcher->OpenDevice(kRequestId1, handler1.get()->AsWeakPtr(), |
| video_device_info.device.id, kVideoType, |
| security_origin); |
| int ipc_request_id2 = dispatcher->next_ipc_id_; |
| EXPECT_NE(ipc_request_id1, ipc_request_id2); |
| dispatcher->OpenDevice(kRequestId2, handler1.get()->AsWeakPtr(), |
| video_device_info.device.id, kVideoType, |
| security_origin); |
| EXPECT_EQ(dispatcher->requests_.size(), size_t(2)); |
| |
| // Complete the OpenDevice of request 1. |
| std::string stream_label1 = std::string("stream1"); |
| dispatcher->OnMessageReceived(MediaStreamMsg_DeviceOpened( |
| kRouteId, ipc_request_id1, stream_label1, video_device_info)); |
| EXPECT_EQ(handler1->request_id_, kRequestId1); |
| |
| // Complete the OpenDevice of request 2. |
| std::string stream_label2 = std::string("stream2"); |
| dispatcher->OnMessageReceived(MediaStreamMsg_DeviceOpened( |
| kRouteId, ipc_request_id2, stream_label2, video_device_info)); |
| EXPECT_EQ(handler1->request_id_, kRequestId2); |
| |
| EXPECT_EQ(dispatcher->requests_.size(), size_t(0)); |
| EXPECT_EQ(dispatcher->label_stream_map_.size(), size_t(2)); |
| |
| // Check the video_session_id. |
| EXPECT_EQ(dispatcher->video_session_id(stream_label1, 0), kVideoSessionId); |
| EXPECT_EQ(dispatcher->video_session_id(stream_label2, 0), kVideoSessionId); |
| |
| // Close the device from request 2. |
| dispatcher->CloseDevice(stream_label2); |
| EXPECT_EQ(dispatcher->video_session_id(stream_label2, 0), |
| StreamDeviceInfo::kNoId); |
| |
| // Close the device from request 1. |
| dispatcher->CloseDevice(stream_label1); |
| EXPECT_EQ(dispatcher->video_session_id(stream_label1, 0), |
| StreamDeviceInfo::kNoId); |
| EXPECT_EQ(dispatcher->label_stream_map_.size(), size_t(0)); |
| |
| // Verify that the request have been completed. |
| EXPECT_EQ(dispatcher->label_stream_map_.size(), size_t(0)); |
| EXPECT_EQ(dispatcher->requests_.size(), size_t(0)); |
| } |
| |
| TEST_F(MediaStreamDispatcherTest, TestFailure) { |
| std::unique_ptr<MediaStreamDispatcher> dispatcher( |
| new MediaStreamDispatcher(NULL)); |
| std::unique_ptr<MockMediaStreamDispatcherEventHandler> handler( |
| new MockMediaStreamDispatcherEventHandler); |
| StreamControls components(true, true); |
| url::Origin security_origin; |
| |
| // Test failure when creating a stream. |
| int ipc_request_id1 = dispatcher->next_ipc_id_; |
| dispatcher->GenerateStream(kRequestId1, handler.get()->AsWeakPtr(), |
| components, security_origin, true); |
| dispatcher->OnMessageReceived(MediaStreamMsg_StreamGenerationFailed( |
| kRouteId, ipc_request_id1, MEDIA_DEVICE_PERMISSION_DENIED)); |
| |
| // Verify that the request have been completed. |
| EXPECT_EQ(handler->request_id_, kRequestId1); |
| EXPECT_EQ(dispatcher->requests_.size(), size_t(0)); |
| |
| // Create a new stream. |
| ipc_request_id1 = dispatcher->next_ipc_id_; |
| dispatcher->GenerateStream(kRequestId1, handler.get()->AsWeakPtr(), |
| components, security_origin, true); |
| |
| StreamDeviceInfoArray audio_device_array(1); |
| StreamDeviceInfo audio_device_info; |
| audio_device_info.device.name = "Microphone"; |
| audio_device_info.device.type = kAudioType; |
| audio_device_info.session_id = kAudioSessionId; |
| audio_device_array[0] = audio_device_info; |
| |
| StreamDeviceInfoArray video_device_array(1); |
| StreamDeviceInfo video_device_info; |
| video_device_info.device.name = "Camera"; |
| video_device_info.device.type = kVideoType; |
| video_device_info.session_id = kVideoSessionId; |
| video_device_array[0] = video_device_info; |
| |
| // Complete the creation of stream1. |
| std::string stream_label1 = std::string("stream1"); |
| dispatcher->OnMessageReceived(MediaStreamMsg_StreamGenerated( |
| kRouteId, ipc_request_id1, stream_label1, |
| audio_device_array, video_device_array)); |
| EXPECT_EQ(handler->request_id_, kRequestId1); |
| EXPECT_EQ(handler->label_, stream_label1); |
| EXPECT_EQ(dispatcher->video_session_id(stream_label1, 0), kVideoSessionId); |
| } |
| |
| TEST_F(MediaStreamDispatcherTest, CancelGenerateStream) { |
| std::unique_ptr<MediaStreamDispatcher> dispatcher( |
| new MediaStreamDispatcher(NULL)); |
| std::unique_ptr<MockMediaStreamDispatcherEventHandler> handler( |
| new MockMediaStreamDispatcherEventHandler); |
| StreamControls components(true, true); |
| int ipc_request_id1 = dispatcher->next_ipc_id_; |
| |
| dispatcher->GenerateStream(kRequestId1, handler.get()->AsWeakPtr(), |
| components, url::Origin(), true); |
| dispatcher->GenerateStream(kRequestId2, handler.get()->AsWeakPtr(), |
| components, url::Origin(), true); |
| |
| EXPECT_EQ(2u, dispatcher->requests_.size()); |
| dispatcher->CancelGenerateStream(kRequestId2, handler.get()->AsWeakPtr()); |
| EXPECT_EQ(1u, dispatcher->requests_.size()); |
| |
| // Complete the creation of stream1. |
| StreamDeviceInfo audio_device_info; |
| audio_device_info.device.name = "Microphone"; |
| audio_device_info.device.type = kAudioType; |
| audio_device_info.session_id = kAudioSessionId; |
| StreamDeviceInfoArray audio_device_array(1); |
| audio_device_array[0] = audio_device_info; |
| |
| StreamDeviceInfo video_device_info; |
| video_device_info.device.name = "Camera"; |
| video_device_info.device.type = kVideoType; |
| video_device_info.session_id = kVideoSessionId; |
| StreamDeviceInfoArray video_device_array(1); |
| video_device_array[0] = video_device_info; |
| |
| std::string stream_label1 = "stream1"; |
| dispatcher->OnMessageReceived(MediaStreamMsg_StreamGenerated( |
| kRouteId, ipc_request_id1, stream_label1, |
| audio_device_array, video_device_array)); |
| EXPECT_EQ(handler->request_id_, kRequestId1); |
| EXPECT_EQ(handler->label_, stream_label1); |
| EXPECT_EQ(0u, dispatcher->requests_.size()); |
| } |
| |
| // Test that the MediaStreamDispatcherEventHandler is notified when the message |
| // MediaStreamMsg_DeviceStopped is received. |
| TEST_F(MediaStreamDispatcherTest, DeviceClosed) { |
| StreamControls controls(true, true); |
| |
| int ipc_request_id = GenerateStream(controls, kRequestId1); |
| const std::string& label = |
| CompleteGenerateStream(ipc_request_id, controls, kRequestId1); |
| |
| dispatcher_->OnMessageReceived( |
| MediaStreamMsg_DeviceStopped(kRouteId, label, handler_->video_device_)); |
| // Verify that MediaStreamDispatcherEventHandler::OnDeviceStopped has been |
| // called. |
| EXPECT_EQ(label, handler_->device_stopped_label_); |
| EXPECT_EQ(dispatcher_->video_session_id(label, 0), |
| StreamDeviceInfo::kNoId); |
| } |
| |
| TEST_F(MediaStreamDispatcherTest, GetNonScreenCaptureDevices) { |
| std::unique_ptr<MediaStreamDispatcher> dispatcher( |
| new MediaStreamDispatcher(nullptr)); |
| std::unique_ptr<MockMediaStreamDispatcherEventHandler> handler( |
| new MockMediaStreamDispatcherEventHandler); |
| url::Origin security_origin; |
| |
| StreamDeviceInfo video_device_info; |
| video_device_info.device.name = "Camera"; |
| video_device_info.device.id = "device_path"; |
| video_device_info.device.type = kVideoType; |
| video_device_info.session_id = kVideoSessionId; |
| |
| StreamDeviceInfo screen_device_info; |
| screen_device_info.device.name = "Screen"; |
| screen_device_info.device.id = "screen_capture"; |
| screen_device_info.device.type = kScreenType; |
| screen_device_info.session_id = kScreenSessionId; |
| |
| EXPECT_EQ(dispatcher->requests_.size(), 0u); |
| EXPECT_EQ(dispatcher->label_stream_map_.size(), 0u); |
| |
| int ipc_request_id1 = dispatcher->next_ipc_id_; |
| dispatcher->OpenDevice(kRequestId1, handler.get()->AsWeakPtr(), |
| video_device_info.device.id, kVideoType, |
| security_origin); |
| int ipc_request_id2 = dispatcher->next_ipc_id_; |
| EXPECT_NE(ipc_request_id1, ipc_request_id2); |
| dispatcher->OpenDevice(kRequestId2, handler.get()->AsWeakPtr(), |
| screen_device_info.device.id, kScreenType, |
| security_origin); |
| EXPECT_EQ(dispatcher->requests_.size(), 2u); |
| |
| // Complete the OpenDevice of request 1. |
| std::string stream_label1 = std::string("stream1"); |
| dispatcher->OnMessageReceived(MediaStreamMsg_DeviceOpened( |
| kRouteId, ipc_request_id1, stream_label1, video_device_info)); |
| EXPECT_EQ(handler->request_id_, kRequestId1); |
| |
| // Complete the OpenDevice of request 2. |
| std::string stream_label2 = std::string("stream2"); |
| dispatcher->OnMessageReceived(MediaStreamMsg_DeviceOpened( |
| kRouteId, ipc_request_id2, stream_label2, screen_device_info)); |
| EXPECT_EQ(handler->request_id_, kRequestId2); |
| |
| EXPECT_EQ(dispatcher->requests_.size(), 0u); |
| EXPECT_EQ(dispatcher->label_stream_map_.size(), 2u); |
| |
| // Only the device with |kVideoType| will be returned. |
| StreamDeviceInfoArray video_device_array = |
| dispatcher->GetNonScreenCaptureDevices(); |
| EXPECT_EQ(video_device_array.size(), 1u); |
| |
| // Close the device from request 2. |
| dispatcher->CloseDevice(stream_label2); |
| EXPECT_EQ(dispatcher->video_session_id(stream_label2, 0), |
| StreamDeviceInfo::kNoId); |
| |
| // Close the device from request 1. |
| dispatcher->CloseDevice(stream_label1); |
| EXPECT_EQ(dispatcher->video_session_id(stream_label1, 0), |
| StreamDeviceInfo::kNoId); |
| |
| // Verify that the request have been completed. |
| EXPECT_EQ(dispatcher->label_stream_map_.size(), 0u); |
| EXPECT_EQ(dispatcher->requests_.size(), 0u); |
| } |
| |
| } // namespace content |