Commit e7934697 authored by Weiyong Yao's avatar Weiyong Yao Committed by Commit Bot

Add a ChangeDevice method to media stream source implementations

This is part of the working for the dynamic change of screenshare
source feature. This cl is to add a ChangeDevice method to change the
audio/video source within the same session.

Note: Add a source/track is not supported in this cl.

Bug: 892201
Change-Id: I99e246456b735dbe66a82686745eb0a7c35a7ea1
Reviewed-on: https://chromium-review.googlesource.com/c/1330717Reviewed-by: default avatarMustafa Emre Acer <meacer@chromium.org>
Reviewed-by: default avatarGuido Urdaneta <guidou@chromium.org>
Reviewed-by: default avatarEmircan Uysaler <emircan@chromium.org>
Commit-Queue: Weiyong Yao <braveyao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611428}
parent 48d7bb29
......@@ -133,6 +133,11 @@ class MockMediaStreamDispatcherHost : public MediaStreamDispatcherHost,
OnDeviceStoppedInternal(label, device);
}
// mojom::MediaStreamDeviceObserver implementation.
void OnDeviceChanged(const std::string& label,
const MediaStreamDevice& old_device,
const MediaStreamDevice& new_device) override {}
mojom::MediaStreamDeviceObserverPtr CreateInterfacePtrAndBind() {
mojom::MediaStreamDeviceObserverPtr observer;
binding_.Bind(mojo::MakeRequest(&observer));
......
......@@ -69,10 +69,13 @@ struct StreamControls {
bool disable_local_echo;
};
// Per-frame renderer-side interface that receives device stopped notifications
// from the browser process.
// Per-frame renderer-side interface that receives device stopped/change
// notifications from the browser process.
interface MediaStreamDeviceObserver {
OnDeviceStopped(string label, MediaStreamDevice device);
OnDeviceChanged(string label,
MediaStreamDevice old_device,
MediaStreamDevice new_device);
};
// Per-frame browser-side interface that is used by the renderer process to
......
......@@ -132,4 +132,14 @@ void LocalMediaStreamAudioSource::OnCaptureMuted(bool is_muted) {
SetMutedState(is_muted);
}
void LocalMediaStreamAudioSource::ChangeSourceImpl(
const MediaStreamDevice& new_device) {
WebRtcLogMessage(
"LocalMediaStreamAudioSource::ChangeSourceImpl(new_device = " +
new_device.id + ")");
EnsureSourceIsStopped();
SetDevice(new_device);
EnsureSourceIsStarted();
}
} // namespace content
......@@ -32,6 +32,9 @@ class CONTENT_EXPORT LocalMediaStreamAudioSource
~LocalMediaStreamAudioSource() final;
// MediaStreamAudioSource implementation.
void ChangeSourceImpl(const MediaStreamDevice& new_device) final;
private:
// MediaStreamAudioSource implementation.
bool EnsureSourceIsStarted() final;
......
......@@ -103,6 +103,16 @@ void* MediaStreamAudioSource::GetClassIdentifier() const {
return nullptr;
}
void MediaStreamAudioSource::DoChangeSource(
const MediaStreamDevice& new_device) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
if (is_stopped_)
return;
ChangeSourceImpl(new_device);
}
std::unique_ptr<MediaStreamAudioTrack>
MediaStreamAudioSource::CreateMediaStreamAudioTrack(const std::string& id) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
......@@ -121,6 +131,13 @@ void MediaStreamAudioSource::EnsureSourceIsStopped() {
DVLOG(1) << "MediaStreamAudioSource@" << this << "::EnsureSourceIsStopped()";
}
void MediaStreamAudioSource::ChangeSourceImpl(
const MediaStreamDevice& new_device) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
DVLOG(1) << "MediaStreamAudioSource@" << this << "::ChangeSourceImpl()";
NOTIMPLEMENTED();
}
void MediaStreamAudioSource::SetFormat(const media::AudioParameters& params) {
DVLOG(1) << "MediaStreamAudioSource@" << this << "::SetFormat("
<< params.AsHumanReadableString() << "), was previously set to {"
......
......@@ -134,6 +134,11 @@ class CONTENT_EXPORT MediaStreamAudioSource : public MediaStreamSource {
// should override this method.
virtual void EnsureSourceIsStopped();
// Stops the source and start the |new_device|.
// A default no-op implementation is provided in this base class. Subclasses
// should override this method.
virtual void ChangeSourceImpl(const MediaStreamDevice& new_device);
// Called by subclasses to update the format of the audio passing through this
// source to the sinks. This may be called at any time, before or after
// tracks have been connected; but must be called at least once before
......@@ -159,6 +164,7 @@ class CONTENT_EXPORT MediaStreamAudioSource : public MediaStreamSource {
private:
// MediaStreamSource override.
void DoStopSource() final;
void DoChangeSource(const MediaStreamDevice& new_device) final;
// Removes |track| from the list of instances that get a copy of the source
// audio data. The "stop callback" that was provided to the track calls
......
......@@ -80,7 +80,7 @@ void MediaStreamDeviceObserver::OnDeviceStopped(
auto it = label_stream_map_.find(label);
if (it == label_stream_map_.end()) {
// This can happen if a user happen stop a the device from JS at the same
// This can happen if a user stops a device from JS at the same
// time as the underlying media device is unplugged from the system.
return;
}
......@@ -106,6 +106,39 @@ void MediaStreamDeviceObserver::OnDeviceStopped(
label_stream_map_.erase(it);
}
void MediaStreamDeviceObserver::OnDeviceChanged(
const std::string& label,
const MediaStreamDevice& old_device,
const MediaStreamDevice& new_device) {
DVLOG(1) << __func__ << " old_device_id=" << old_device.id
<< " new_device_id=" << new_device.id;
DCHECK(thread_checker_.CalledOnValidThread());
auto it = label_stream_map_.find(label);
if (it == label_stream_map_.end()) {
// This can happen if a user stops a device from JS at the same
// time as the underlying media device is unplugged from the system.
return;
}
Stream* stream = &it->second;
if (stream->handler.get())
stream->handler->OnDeviceChanged(old_device, new_device);
// Update device list only for device changing. Removing device will be
// handled in its own callback.
if (old_device.type != MEDIA_NO_SERVICE &&
new_device.type != MEDIA_NO_SERVICE) {
if (RemoveStreamDeviceFromArray(old_device, &stream->audio_devices) ||
RemoveStreamDeviceFromArray(old_device, &stream->video_devices)) {
if (IsAudioInputMediaType(new_device.type))
stream->audio_devices.push_back(new_device);
else
stream->video_devices.push_back(new_device);
}
}
}
void MediaStreamDeviceObserver::BindMediaStreamDeviceObserverRequest(
mojom::MediaStreamDeviceObserverRequest request) {
binding_.Bind(std::move(request));
......
......@@ -57,6 +57,8 @@ class CONTENT_EXPORT MediaStreamDeviceObserver
private:
FRIEND_TEST_ALL_PREFIXES(MediaStreamDeviceObserverTest,
GetNonScreenCaptureDevices);
FRIEND_TEST_ALL_PREFIXES(MediaStreamDeviceObserverTest, OnDeviceStopped);
FRIEND_TEST_ALL_PREFIXES(MediaStreamDeviceObserverTest, OnDeviceChanged);
// Private class for keeping track of opened devices and who have
// opened it.
......@@ -71,6 +73,9 @@ class CONTENT_EXPORT MediaStreamDeviceObserver
// mojom::MediaStreamDeviceObserver implementation.
void OnDeviceStopped(const std::string& label,
const MediaStreamDevice& device) override;
void OnDeviceChanged(const std::string& label,
const MediaStreamDevice& old_device,
const MediaStreamDevice& new_device) override;
void BindMediaStreamDeviceObserverRequest(
mojom::MediaStreamDeviceObserverRequest request);
......
......@@ -29,6 +29,7 @@ class MediaStreamDeviceObserverTest : public ::testing::Test {
const MediaStreamDevice& device) {
if (success) {
stream_label_ = label;
current_device_ = device;
observer_->AddStream(label, device);
}
......@@ -40,6 +41,7 @@ class MediaStreamDeviceObserverTest : public ::testing::Test {
base::test::ScopedTaskEnvironment scoped_task_environment_;
MockMojoMediaStreamDispatcherHost mock_dispatcher_host_;
std::unique_ptr<MediaStreamDeviceObserver> observer_;
MediaStreamDevice current_device_;
};
TEST_F(MediaStreamDeviceObserverTest, GetNonScreenCaptureDevices) {
......@@ -87,4 +89,68 @@ TEST_F(MediaStreamDeviceObserverTest, GetNonScreenCaptureDevices) {
EXPECT_EQ(observer_->label_stream_map_.size(), 0u);
}
TEST_F(MediaStreamDeviceObserverTest, OnDeviceStopped) {
const int kRequestId = 5;
EXPECT_EQ(observer_->label_stream_map_.size(), 0u);
// OpenDevice request.
base::RunLoop run_loop1;
mock_dispatcher_host_.OpenDevice(
kRequestId, "device_path", MEDIA_DEVICE_VIDEO_CAPTURE,
base::BindOnce(&MediaStreamDeviceObserverTest::OnDeviceOpened,
base::Unretained(this), run_loop1.QuitClosure()));
run_loop1.Run();
EXPECT_EQ(observer_->label_stream_map_.size(), 1u);
observer_->OnDeviceStopped(stream_label_, current_device_);
// Verify that the request have been completed.
EXPECT_EQ(observer_->label_stream_map_.size(), 0u);
}
TEST_F(MediaStreamDeviceObserverTest, OnDeviceChanged) {
const int kRequestId1 = 5;
const int kRequestId2 = 7;
const std::string example_video_id1 = "fake_video_device1";
const std::string example_video_id2 = "fake_video_device2";
EXPECT_EQ(observer_->label_stream_map_.size(), 0u);
// OpenDevice request.
base::RunLoop run_loop1;
mock_dispatcher_host_.OpenDevice(
kRequestId1, example_video_id1, MEDIA_DEVICE_VIDEO_CAPTURE,
base::BindOnce(&MediaStreamDeviceObserverTest::OnDeviceOpened,
base::Unretained(this), run_loop1.QuitClosure()));
run_loop1.Run();
EXPECT_EQ(observer_->label_stream_map_.size(), 1u);
MediaStreamDevices video_devices = observer_->GetNonScreenCaptureDevices();
EXPECT_EQ(video_devices.size(), 1u);
EXPECT_EQ(video_devices[0].id, example_video_id1);
// OnDeviceChange request.
MediaStreamDevice fake_video_device(content::MEDIA_DEVICE_VIDEO_CAPTURE,
example_video_id2, "Fake Video Device");
fake_video_device.session_id = kRequestId2;
observer_->OnDeviceChanged(stream_label_, current_device_, fake_video_device);
// Verify that the device has been changed to the new |fake_video_device|.
EXPECT_EQ(observer_->label_stream_map_.size(), 1u);
video_devices = observer_->GetNonScreenCaptureDevices();
EXPECT_EQ(video_devices.size(), 1u);
EXPECT_EQ(video_devices[0].id, example_video_id2);
EXPECT_EQ(video_devices[0].session_id, kRequestId2);
// Close the device from request.
observer_->RemoveStream(stream_label_);
EXPECT_EQ(observer_->video_session_id(stream_label_),
MediaStreamDevice::kNoId);
// Verify that the request have been completed.
EXPECT_EQ(observer_->label_stream_map_.size(), 0u);
}
} // namespace content
......@@ -17,6 +17,10 @@ class CONTENT_EXPORT MediaStreamDispatcherEventHandler {
// A device has been stopped in the browser process.
virtual void OnDeviceStopped(const MediaStreamDevice& device) = 0;
// Switch to the new device within the working session.
virtual void OnDeviceChanged(const MediaStreamDevice& old_device,
const MediaStreamDevice& new_device) = 0;
protected:
virtual ~MediaStreamDispatcherEventHandler() {}
};
......
......@@ -64,4 +64,9 @@ void MediaStreamSource::ResetSourceStoppedCallback() {
stop_callback_.Reset();
}
void MediaStreamSource::ChangeSource(const MediaStreamDevice& new_device) {
DCHECK(thread_checker_.CalledOnValidThread());
DoChangeSource(new_device);
}
} // namespace content
......@@ -60,11 +60,18 @@ class CONTENT_EXPORT MediaStreamSource
// in the future.
void ResetSourceStoppedCallback();
// Change the source to the |new_device| by calling DoChangeSource().
void ChangeSource(const MediaStreamDevice& new_device);
protected:
// Called when StopSource is called. It allows derived classes to implement
// its own Stop method.
virtual void DoStopSource() = 0;
// Called when ChangeSource is called. It allows derived class to implement
// it's own ChangeSource method.
virtual void DoChangeSource(const MediaStreamDevice& new_device) = 0;
// Runs the stop callback (if set) and sets the
// WebMediaStreamSource::readyState to ended. This can be used by
// implementations to implement custom stop methods.
......
......@@ -184,12 +184,20 @@ MediaStreamVideoCapturerSource::MediaStreamVideoCapturerSource(
SetStopCallback(stop_callback);
SetDevice(device);
SetDeviceRotationDetection(true /* enabled */);
device_video_capturer_factory_callback_ = base::BindRepeating(
&MediaStreamVideoCapturerSource::RecreateLocalVideoCapturerSource);
}
MediaStreamVideoCapturerSource::~MediaStreamVideoCapturerSource() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void MediaStreamVideoCapturerSource::
SetDeviceVideoCapturerFactoryCallbackForTesting(
DeviceVideoCapturerFactoryCallback testing_factory_callback) {
device_video_capturer_factory_callback_ = std::move(testing_factory_callback);
}
void MediaStreamVideoCapturerSource::RequestRefreshFrame() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
source_->RequestRefreshFrame();
......@@ -267,6 +275,25 @@ MediaStreamVideoCapturerSource::GetCurrentCaptureParams() const {
return capture_params_;
}
void MediaStreamVideoCapturerSource::ChangeSourceImpl(
const MediaStreamDevice& new_device) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(device_video_capturer_factory_callback_);
if (state_ != STARTED) {
return;
}
state_ = STOPPING_FOR_CHANGE_SOURCE;
source_->StopCapture();
SetDevice(new_device);
source_ = device_video_capturer_factory_callback_.Run(new_device.session_id);
source_->StartCapture(
capture_params_, frame_callback_,
base::BindRepeating(&MediaStreamVideoCapturerSource::OnRunStateChanged,
base::Unretained(this), capture_params_));
}
void MediaStreamVideoCapturerSource::OnRunStateChanged(
const media::VideoCaptureParams& new_capture_params,
bool is_running) {
......@@ -292,6 +319,9 @@ void MediaStreamVideoCapturerSource::OnRunStateChanged(
state_ = is_running ? STARTED : STOPPED;
OnStopForRestartDone(!is_running);
break;
case STOPPING_FOR_CHANGE_SOURCE:
state_ = is_running ? STARTED : STOPPED;
break;
case RESTARTING:
if (is_running) {
state_ = STARTED;
......@@ -317,4 +347,11 @@ MediaStreamVideoCapturerSource::GetMediaStreamDispatcherHost(
return dispatcher_host_;
}
// static
std::unique_ptr<media::VideoCapturerSource>
MediaStreamVideoCapturerSource::RecreateLocalVideoCapturerSource(
int session_id) {
return std::make_unique<LocalVideoCapturerSource>(session_id);
}
} // namespace content
......@@ -40,6 +40,12 @@ class CONTENT_EXPORT MediaStreamVideoCapturerSource
const media::VideoCaptureParams& capture_params);
~MediaStreamVideoCapturerSource() override;
using DeviceVideoCapturerFactoryCallback =
base::RepeatingCallback<std::unique_ptr<media::VideoCapturerSource>(
int session_id)>;
void SetDeviceVideoCapturerFactoryCallbackForTesting(
DeviceVideoCapturerFactoryCallback testing_factory_callback);
private:
friend class CanvasCaptureHandlerTest;
friend class MediaStreamVideoCapturerSourceTest;
......@@ -47,6 +53,7 @@ class CONTENT_EXPORT MediaStreamVideoCapturerSource
FRIEND_TEST_ALL_PREFIXES(MediaStreamVideoCapturerSourceTest, StartAndStop);
FRIEND_TEST_ALL_PREFIXES(MediaStreamVideoCapturerSourceTest,
CaptureTimeAndMetadataPlumbing);
FRIEND_TEST_ALL_PREFIXES(MediaStreamVideoCapturerSourceTest, ChangeSource);
// MediaStreamVideoSource overrides.
void RequestRefreshFrame() override;
......@@ -60,6 +67,7 @@ class CONTENT_EXPORT MediaStreamVideoCapturerSource
base::Optional<media::VideoCaptureFormat> GetCurrentFormat() const override;
base::Optional<media::VideoCaptureParams> GetCurrentCaptureParams()
const override;
void ChangeSourceImpl(const MediaStreamDevice& new_device) override;
// Method to bind as RunningCallback in VideoCapturerSource::StartCapture().
void OnRunStateChanged(const media::VideoCaptureParams& new_capture_params,
......@@ -68,18 +76,29 @@ class CONTENT_EXPORT MediaStreamVideoCapturerSource
const mojom::MediaStreamDispatcherHostPtr& GetMediaStreamDispatcherHost(
RenderFrame* render_frame);
static std::unique_ptr<media::VideoCapturerSource>
RecreateLocalVideoCapturerSource(int session_id);
mojom::MediaStreamDispatcherHostPtr dispatcher_host_;
int render_frame_id_;
// The source that provides video frames.
const std::unique_ptr<media::VideoCapturerSource> source_;
enum State { STARTING, STARTED, STOPPING_FOR_RESTART, RESTARTING, STOPPED };
std::unique_ptr<media::VideoCapturerSource> source_;
enum State {
STARTING,
STARTED,
STOPPING_FOR_RESTART,
STOPPING_FOR_CHANGE_SOURCE,
RESTARTING,
STOPPED
};
State state_ = STOPPED;
media::VideoCaptureParams capture_params_;
VideoCaptureDeliverFrameCB frame_callback_;
DeviceVideoCapturerFactoryCallback device_video_capturer_factory_callback_;
DISALLOW_COPY_AND_ASSIGN(MediaStreamVideoCapturerSource);
};
......
......@@ -49,7 +49,6 @@ class MockVideoCapturerSource : public media::VideoCapturerSource {
}
void StopCapture() override {
MockStopCapture();
SetRunning(false);
}
void SetRunning(bool is_running) {
blink::scheduler::GetSingleThreadTaskRunnerForTesting()->PostTask(
......@@ -122,6 +121,12 @@ class MediaStreamVideoCapturerSourceTest : public testing::Test {
false /* remote */);
webkit_source_.SetExtraData(source_);
webkit_source_id_ = webkit_source_.Id();
MediaStreamVideoCapturerSource::DeviceVideoCapturerFactoryCallback
callback = base::BindRepeating(
&MediaStreamVideoCapturerSourceTest::RecreateVideoCapturerSource,
base::Unretained(this));
source_->SetDeviceVideoCapturerFactoryCallbackForTesting(callback);
}
void TearDown() override {
......@@ -158,6 +163,14 @@ class MediaStreamVideoCapturerSourceTest : public testing::Test {
MOCK_METHOD0(MockNotification, void());
std::unique_ptr<media::VideoCapturerSource> RecreateVideoCapturerSource(
int session_id) {
auto delegate = std::make_unique<MockVideoCapturerSource>();
delegate_ = delegate.get();
EXPECT_CALL(*delegate_, MockStartCapture(_, _, _));
return delegate;
}
protected:
void OnConstraintsApplied(MediaStreamSource* source,
MediaStreamRequestResult result,
......@@ -354,4 +367,42 @@ TEST_F(MediaStreamVideoCapturerSourceTest, StartStopAndNotify) {
base::RunLoop().RunUntilIdle();
}
TEST_F(MediaStreamVideoCapturerSourceTest, ChangeSource) {
InSequence s;
EXPECT_CALL(mock_delegate(), MockStartCapture(_, _, _));
blink::WebMediaStreamTrack track =
StartSource(VideoTrackAdapterSettings(), base::nullopt, false, 0.0);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(blink::WebMediaStreamSource::kReadyStateLive,
webkit_source_.GetReadyState());
EXPECT_FALSE(source_stopped_);
// A bogus notification of running from the delegate when the source has
// already started should not change the state.
delegate_->SetRunning(true);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(blink::WebMediaStreamSource::kReadyStateLive,
webkit_source_.GetReadyState());
EXPECT_FALSE(source_stopped_);
// |ChangeSourceImpl()| will recreate the |delegate_|, so check the
// |MockStartCapture()| invoking in the |RecreateVideoCapturerSource()|.
EXPECT_CALL(mock_delegate(), MockStopCapture());
MediaStreamDevice fake_video_device(MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
"Fake_Video_Device", "Fake Video Device");
source_->ChangeSourceImpl(fake_video_device);
EXPECT_EQ(blink::WebMediaStreamSource::kReadyStateLive,
webkit_source_.GetReadyState());
EXPECT_FALSE(source_stopped_);
// If the delegate stops, the source should stop.
EXPECT_CALL(mock_delegate(), MockStopCapture());
delegate_->SetRunning(false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(blink::WebMediaStreamSource::kReadyStateEnded,
webkit_source_.GetReadyState());
// Verify that MediaStreamSource::SourceStoppedCallback has been triggered.
EXPECT_TRUE(source_stopped_);
}
} // namespace content
......@@ -311,6 +311,19 @@ MediaStreamVideoSource::GetCurrentCaptureParams() const {
return base::Optional<media::VideoCaptureParams>();
}
void MediaStreamVideoSource::DoChangeSource(
const MediaStreamDevice& new_device) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(1) << "MediaStreamVideoSource::DoChangeSource: "
<< ", new device id = " << new_device.id
<< ", session id = " << new_device.session_id;
if (state_ != STARTED) {
return;
}
ChangeSourceImpl(new_device);
}
void MediaStreamVideoSource::DoStopSource() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(3) << "DoStopSource()";
......
......@@ -151,6 +151,8 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
}
protected:
// MediaStreamSource implementation.
void DoChangeSource(const MediaStreamDevice& new_device) override;
void DoStopSource() override;
// Sets ready state and notifies the ready state to all registered tracks.
......@@ -226,6 +228,9 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
// has become secure or insecure.
virtual void OnCapturingLinkSecured(bool is_secure) {}
// Optionally overridden by subclasses to implement changing source.
virtual void ChangeSourceImpl(const MediaStreamDevice& new_device) {}
enum State {
NEW,
STARTING,
......
......@@ -61,6 +61,11 @@ void MockMediaStreamVideoSource::OnHasConsumers(bool has_consumers) {
is_suspended_ = !has_consumers;
}
void MockMediaStreamVideoSource::DoChangeSource(
const MediaStreamDevice& new_device) {
ChangeSourceImpl(new_device);
}
void MockMediaStreamVideoSource::StartSourceImpl(
const VideoCaptureDeliverFrameCB& frame_callback) {
DCHECK(frame_callback_.is_null());
......
......@@ -62,6 +62,9 @@ class MockMediaStreamVideoSource : public MediaStreamVideoSource {
void OnHasConsumers(bool has_consumers) override;
protected:
// Implements MediaStreamSource.
void DoChangeSource(const MediaStreamDevice& new_device) override;
// Implements MediaStreamVideoSource.
void StartSourceImpl(
const VideoCaptureDeliverFrameCB& frame_callback) override;
......
......@@ -34,7 +34,7 @@ void MockMojoMediaStreamDispatcherHost::GenerateStream(
MediaStreamDevice audio_device;
audio_device.id = controls.audio.device_id + base::IntToString(session_id_);
audio_device.name = "microphone";
audio_device.type = MEDIA_DEVICE_AUDIO_CAPTURE;
audio_device.type = controls.audio.stream_type;
audio_device.session_id = session_id_;
audio_device.matched_output_device_id =
"associated_output_device_id" + base::IntToString(request_id_);
......@@ -45,7 +45,7 @@ void MockMojoMediaStreamDispatcherHost::GenerateStream(
MediaStreamDevice video_device;
video_device.id = controls.video.device_id + base::IntToString(session_id_);
video_device.name = "usb video camera";
video_device.type = MEDIA_DEVICE_VIDEO_CAPTURE;
video_device.type = controls.video.stream_type;
video_device.video_facing = media::MEDIA_VIDEO_FACING_USER;
video_device.session_id = session_id_;
video_devices_.push_back(video_device);
......
......@@ -39,6 +39,7 @@
#include "third_party/blink/public/web/web_heap.h"
using testing::_;
using testing::Mock;
namespace content {
......@@ -135,6 +136,18 @@ void CheckVideoSourceAndTrack(MediaStreamVideoSource* source,
EXPECT_EQ(settings.frame_rate, expected_track_frame_rate);
}
class MockLocalMediaStreamAudioSource : public MediaStreamAudioSource {
public:
MockLocalMediaStreamAudioSource()
: MediaStreamAudioSource(true /* is_local_source */) {}
MOCK_METHOD0(EnsureSourceIsStopped, void());
void ChangeSourceImpl(const MediaStreamDevice& new_device) {
EnsureSourceIsStopped();
}
};
class MockMediaStreamVideoCapturerSource : public MockMediaStreamVideoSource {
public:
MockMediaStreamVideoCapturerSource(const MediaStreamDevice& device,
......@@ -144,6 +157,8 @@ class MockMediaStreamVideoCapturerSource : public MockMediaStreamVideoSource {
SetDevice(device);
SetStopCallback(stop_callback);
}
MOCK_METHOD1(ChangeSourceImpl, void(const MediaStreamDevice& new_device));
};
const char kInvalidDeviceId[] = "invalid";
......@@ -300,6 +315,10 @@ class UserMediaProcessorUnderTest : public UserMediaProcessor {
MockMediaStreamVideoCapturerSource* last_created_video_source() const {
return video_source_;
}
MockLocalMediaStreamAudioSource* last_created_local_audio_source() const {
return local_audio_source_;
}
void SetCreateSourceThatFails(bool should_fail) {
create_source_that_fails_ = should_fail;
}
......@@ -342,6 +361,9 @@ class UserMediaProcessorUnderTest : public UserMediaProcessor {
bool EnsureSourceIsStarted() override { return false; }
};
source = new FailedAtLifeAudioSource();
} else if (IsDesktopCaptureMediaType(device.type)) {
local_audio_source_ = new MockLocalMediaStreamAudioSource();
source = local_audio_source_;
} else {
source = new MediaStreamAudioSource(true);
}
......@@ -385,6 +407,7 @@ class UserMediaProcessorUnderTest : public UserMediaProcessor {
PeerConnectionDependencyFactory* factory_;
blink::mojom::MediaDevicesDispatcherHostPtr media_devices_dispatcher_;
MockMediaStreamVideoCapturerSource* video_source_ = nullptr;
MockLocalMediaStreamAudioSource* local_audio_source_ = nullptr;
bool create_source_that_fails_ = false;
blink::WebMediaStream last_generated_stream_;
content::MediaStreamRequestResult result_ = NUM_MEDIA_REQUEST_RESULTS;
......@@ -1347,4 +1370,75 @@ TEST_F(UserMediaClientImplTest, IsCapturing) {
EXPECT_FALSE(user_media_client_impl_->IsCapturing());
}
TEST_F(UserMediaClientImplTest, DesktopCaptureChangeSource) {
MockConstraintFactory factory;
factory.basic().media_stream_source.SetExact(
blink::WebString::FromASCII(kMediaStreamSourceDesktop));
blink::WebMediaConstraints audio_constraints =
factory.CreateWebMediaConstraints();
blink::WebMediaConstraints video_constraints =
factory.CreateWebMediaConstraints();
blink::WebUserMediaRequest request =
blink::WebUserMediaRequest::CreateForTesting(audio_constraints,
video_constraints);
user_media_client_impl_->RequestUserMediaForTest(request);
// Test changing video source.
MockMediaStreamVideoCapturerSource* video_source =
user_media_processor_->last_created_video_source();
MediaStreamDevice fake_video_device(MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
kFakeVideoInputDeviceId1,
"Fake Video Device");
EXPECT_CALL(*video_source, ChangeSourceImpl(_));
user_media_processor_->OnDeviceChanged(video_source->device(),
fake_video_device);
// Test changing audio source.
MockLocalMediaStreamAudioSource* audio_source =
user_media_processor_->last_created_local_audio_source();
EXPECT_NE(audio_source, nullptr);
MediaStreamDevice fake_audio_device(MEDIA_GUM_DESKTOP_AUDIO_CAPTURE,
kFakeVideoInputDeviceId1,
"Fake Audio Device");
EXPECT_CALL(*audio_source, EnsureSourceIsStopped()).Times(2);
user_media_processor_->OnDeviceChanged(audio_source->device(),
fake_audio_device);
user_media_client_impl_->CancelUserMediaRequest(request);
base::RunLoop().RunUntilIdle();
}
TEST_F(UserMediaClientImplTest, DesktopCaptureChangeSourceWithoutAudio) {
MockConstraintFactory factory;
factory.basic().media_stream_source.SetExact(
blink::WebString::FromASCII(kMediaStreamSourceDesktop));
blink::WebMediaConstraints audio_constraints =
factory.CreateWebMediaConstraints();
blink::WebMediaConstraints video_constraints =
factory.CreateWebMediaConstraints();
blink::WebUserMediaRequest request =
blink::WebUserMediaRequest::CreateForTesting(audio_constraints,
video_constraints);
user_media_client_impl_->RequestUserMediaForTest(request);
EXPECT_EQ(1U, mock_dispatcher_host_.audio_devices().size());
EXPECT_EQ(1U, mock_dispatcher_host_.video_devices().size());
// If the new desktop capture source doesn't have audio, the previous audio
// device should be stopped. Here |EnsureSourceIsStopped()| should be called
// only once by |OnDeviceChanged()|.
MockLocalMediaStreamAudioSource* audio_source =
user_media_processor_->last_created_local_audio_source();
EXPECT_NE(audio_source, nullptr);
EXPECT_CALL(*audio_source, EnsureSourceIsStopped()).Times(1);
MediaStreamDevice fake_audio_device(MEDIA_NO_SERVICE, "", "");
user_media_processor_->OnDeviceChanged(audio_source->device(),
fake_audio_device);
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClearExpectations(audio_source);
EXPECT_CALL(*audio_source, EnsureSourceIsStopped()).Times(0);
user_media_client_impl_->CancelUserMediaRequest(request);
base::RunLoop().RunUntilIdle();
}
} // namespace content
......@@ -816,7 +816,6 @@ void UserMediaProcessor::OnAudioSourceStarted(
NotifyCurrentRequestInfoOfAudioSourceStarted(source, result, result_name);
return;
}
NOTREACHED();
}
void UserMediaProcessor::NotifyCurrentRequestInfoOfAudioSourceStarted(
......@@ -850,7 +849,7 @@ void UserMediaProcessor::OnDeviceStopped(const MediaStreamDevice& device) {
const blink::WebMediaStreamSource* source_ptr = FindLocalSource(device);
if (!source_ptr) {
// This happens if the same device is used in several guM requests or
// if a user happen stop a track from JS at the same time
// if a user happens to stop a track from JS at the same time
// as the underlying media device is unplugged from the system.
return;
}
......@@ -861,6 +860,41 @@ void UserMediaProcessor::OnDeviceStopped(const MediaStreamDevice& device) {
RemoveLocalSource(source);
}
void UserMediaProcessor::OnDeviceChanged(const MediaStreamDevice& old_device,
const MediaStreamDevice& new_device) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(1) << "UserMediaClientImpl::OnDeviceChange("
<< "{old_device_id = " << old_device.id
<< ", session id = " << old_device.session_id
<< ", type = " << old_device.type << "}"
<< "{new_device_id = " << new_device.id
<< ", session id = " << new_device.session_id
<< ", type = " << new_device.type << "})";
const blink::WebMediaStreamSource* source_ptr = FindLocalSource(old_device);
if (!source_ptr) {
// This happens if the same device is used in several guM requests or
// if a user happens to stop a track from JS at the same time
// as the underlying media device is unplugged from the system.
DVLOG(1) << "failed to find existing source with device " << old_device.id;
return;
}
if (old_device.type != MEDIA_NO_SERVICE &&
new_device.type == MEDIA_NO_SERVICE) {
// At present, this will only happen to the case that a new desktop capture
// source without audio share is selected, then the previous audio capture
// device should be stopped if existing.
DCHECK(IsAudioInputMediaType(old_device.type));
OnDeviceStopped(old_device);
return;
}
MediaStreamSource* const source_impl =
static_cast<MediaStreamSource*>(source_ptr->GetExtraData());
source_impl->ChangeSource(new_device);
}
blink::WebMediaStreamSource UserMediaProcessor::InitializeVideoSourceObject(
const MediaStreamDevice& device) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......
......@@ -109,6 +109,8 @@ class CONTENT_EXPORT UserMediaProcessor
// MediaStreamDispatcherEventHandler implementation.
void OnDeviceStopped(const MediaStreamDevice& device) override;
void OnDeviceChanged(const MediaStreamDevice& old_device,
const MediaStreamDevice& new_device) override;
void set_media_stream_dispatcher_host_for_testing(
mojom::MediaStreamDispatcherHostPtr dispatcher_host) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment