Commit a58c8bd6 authored by Guido Urdaneta's avatar Guido Urdaneta Committed by Commit Bot

Serialize MediaStreamTrack.stop() with getUserMedia()

When multiple getUserMedia and stop() requests are active concurrently, 
getUserMedia() sometimes fails with TrackStartError.
The reason is that the underlying video capture subsystem does not keep
a count of how many sources have opened a video device.
It can happen that a stop request stops video capture while a 
getUserMedia() request is being processed, with the result being that
the video device is closed before getUserMedia() starts the new video
track, resulting in an error.

One solution to this problem is to make sure that stop requests and
getUserMedia() requests cannot run concurrently, which is the approach
followed by this CL.

This CL basically moves handling of MediaStramTrack.stop() from 
MediaStreamCenter to UserMediaClient, and puts stop() requests in the
same queue used for getUserMedia() and applyConstraints() requests.


Bug: 778039
Change-Id: Id7e0ee38e40265fd599dc6dd0712f0b667d2e1a5
Reviewed-on: https://chromium-review.googlesource.com/751981Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarHenrik Boström <hbos@chromium.org>
Commit-Queue: Guido Urdaneta <guidou@chromium.org>
Cr-Commit-Position: refs/heads/master@{#515494}
parent 7facf42c
...@@ -812,4 +812,12 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, ...@@ -812,4 +812,12 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest,
ExecuteJavascriptAndWaitForOk("applyConstraintsNonDevice()"); ExecuteJavascriptAndWaitForOk("applyConstraintsNonDevice()");
} }
IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest,
ConcurrentGetUserMediaStop) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
NavigateToURL(shell(), url);
ExecuteJavascriptAndWaitForOk("concurrentGetUserMediaStop()");
}
} // namespace content } // namespace content
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "content/renderer/media/media_stream_audio_track.h" #include "content/renderer/media/media_stream_audio_track.h"
#include <utility>
#include <vector> #include <vector>
#include "base/callback_helpers.h" #include "base/callback_helpers.h"
...@@ -102,7 +103,7 @@ void MediaStreamAudioTrack::Start(const base::Closure& stop_callback) { ...@@ -102,7 +103,7 @@ void MediaStreamAudioTrack::Start(const base::Closure& stop_callback) {
stop_callback_ = stop_callback; stop_callback_ = stop_callback;
} }
void MediaStreamAudioTrack::Stop() { void MediaStreamAudioTrack::StopAndNotify(base::OnceClosure callback) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
DVLOG(1) << "Stopping MediaStreamAudioTrack@" << this << '.'; DVLOG(1) << "Stopping MediaStreamAudioTrack@" << this << '.';
...@@ -116,6 +117,8 @@ void MediaStreamAudioTrack::Stop() { ...@@ -116,6 +117,8 @@ void MediaStreamAudioTrack::Stop() {
sink->OnReadyStateChanged(blink::WebMediaStreamSource::kReadyStateEnded); sink->OnReadyStateChanged(blink::WebMediaStreamSource::kReadyStateEnded);
} }
if (callback)
std::move(callback).Run();
weak_factory_.InvalidateWeakPtrs(); weak_factory_.InvalidateWeakPtrs();
} }
......
...@@ -61,7 +61,7 @@ class CONTENT_EXPORT MediaStreamAudioTrack : public MediaStreamTrack { ...@@ -61,7 +61,7 @@ class CONTENT_EXPORT MediaStreamAudioTrack : public MediaStreamTrack {
// Halts the flow of audio data from the source (and to the sinks), and then // Halts the flow of audio data from the source (and to the sinks), and then
// notifies all sinks of the "ended" state. // notifies all sinks of the "ended" state.
void Stop() final; void StopAndNotify(base::OnceClosure callback) final;
// MediaStreamTrack override. // MediaStreamTrack override.
void SetEnabled(bool enabled) override; void SetEnabled(bool enabled) override;
......
...@@ -154,14 +154,6 @@ void MediaStreamCenter::DidDisableMediaStreamTrack( ...@@ -154,14 +154,6 @@ void MediaStreamCenter::DidDisableMediaStreamTrack(
native_track->SetEnabled(false); native_track->SetEnabled(false);
} }
bool MediaStreamCenter::DidStopMediaStreamTrack(
const blink::WebMediaStreamTrack& track) {
DVLOG(1) << "MediaStreamCenter::didStopMediaStreamTrack";
MediaStreamTrack* native_track = MediaStreamTrack::GetTrack(track);
native_track->Stop();
return true;
}
blink::WebAudioSourceProvider* blink::WebAudioSourceProvider*
MediaStreamCenter::CreateWebAudioSourceFromMediaStreamTrack( MediaStreamCenter::CreateWebAudioSourceFromMediaStreamTrack(
const blink::WebMediaStreamTrack& track) { const blink::WebMediaStreamTrack& track) {
......
...@@ -46,9 +46,6 @@ class CONTENT_EXPORT MediaStreamCenter : public blink::WebMediaStreamCenter { ...@@ -46,9 +46,6 @@ class CONTENT_EXPORT MediaStreamCenter : public blink::WebMediaStreamCenter {
void DidDisableMediaStreamTrack( void DidDisableMediaStreamTrack(
const blink::WebMediaStreamTrack& track) override; const blink::WebMediaStreamTrack& track) override;
bool DidStopMediaStreamTrack(
const blink::WebMediaStreamTrack& track) override;
blink::WebAudioSourceProvider* CreateWebAudioSourceFromMediaStreamTrack( blink::WebAudioSourceProvider* CreateWebAudioSourceFromMediaStreamTrack(
const blink::WebMediaStreamTrack& track) override; const blink::WebMediaStreamTrack& track) override;
......
...@@ -22,6 +22,10 @@ MediaStreamSource::~MediaStreamSource() { ...@@ -22,6 +22,10 @@ MediaStreamSource::~MediaStreamSource() {
void MediaStreamSource::StopSource() { void MediaStreamSource::StopSource() {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
DoStopSource(); DoStopSource();
FinalizeStopSource();
}
void MediaStreamSource::FinalizeStopSource() {
if (!stop_callback_.is_null()) if (!stop_callback_.is_null())
base::ResetAndReturn(&stop_callback_).Run(Owner()); base::ResetAndReturn(&stop_callback_).Run(Owner());
Owner().SetReadyState(blink::WebMediaStreamSource::kReadyStateEnded); Owner().SetReadyState(blink::WebMediaStreamSource::kReadyStateEnded);
......
...@@ -36,9 +36,7 @@ class CONTENT_EXPORT MediaStreamSource ...@@ -36,9 +36,7 @@ class CONTENT_EXPORT MediaStreamSource
// JavaScript call to GetUserMedia, e.g., a camera or microphone. // JavaScript call to GetUserMedia, e.g., a camera or microphone.
const MediaStreamDevice& device() const { return device_; } const MediaStreamDevice& device() const { return device_; }
// Stops the source (by calling DoStopSource()). This runs the // Stops the source (by calling DoStopSource()) and runs FinalizeStopSource().
// |stop_callback_| (if set), and then sets the
// WebMediaStreamSource::readyState to ended.
void StopSource(); void StopSource();
// Sets the source's state to muted or to live. // Sets the source's state to muted or to live.
...@@ -60,6 +58,11 @@ class CONTENT_EXPORT MediaStreamSource ...@@ -60,6 +58,11 @@ class CONTENT_EXPORT MediaStreamSource
// its own Stop method. // its own Stop method.
virtual void DoStopSource() = 0; virtual void DoStopSource() = 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.
void FinalizeStopSource();
private: private:
MediaStreamDevice device_; MediaStreamDevice device_;
SourceStoppedCallback stop_callback_; SourceStoppedCallback stop_callback_;
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <string> #include <string>
#include "base/callback.h"
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/threading/thread_checker.h" #include "base/threading/thread_checker.h"
...@@ -32,7 +33,10 @@ class CONTENT_EXPORT MediaStreamTrack ...@@ -32,7 +33,10 @@ class CONTENT_EXPORT MediaStreamTrack
virtual void SetContentHint( virtual void SetContentHint(
blink::WebMediaStreamTrack::ContentHintType content_hint) = 0; blink::WebMediaStreamTrack::ContentHintType content_hint) = 0;
virtual void Stop() = 0; // If |callback| is not null, it is invoked when the track has stopped.
virtual void StopAndNotify(base::OnceClosure callback) = 0;
void Stop() { StopAndNotify(base::OnceClosure()); }
// TODO(hta): Make method pure virtual when all tracks have the method. // TODO(hta): Make method pure virtual when all tracks have the method.
void GetSettings(blink::WebMediaStreamTrack::Settings& settings) override {} void GetSettings(blink::WebMediaStreamTrack::Settings& settings) override {}
......
...@@ -153,6 +153,10 @@ class MediaStreamVideoCapturerSourceTest : public testing::Test { ...@@ -153,6 +153,10 @@ class MediaStreamVideoCapturerSourceTest : public testing::Test {
source_->OnRunStateChanged(delegate_->capture_params(), result); source_->OnRunStateChanged(delegate_->capture_params(), result);
} }
void SetStopCaptureFlag() { stop_capture_flag_ = true; }
MOCK_METHOD0(MockNotification, void());
protected: protected:
void OnConstraintsApplied(MediaStreamSource* source, void OnConstraintsApplied(MediaStreamSource* source,
MediaStreamRequestResult result, MediaStreamRequestResult result,
...@@ -170,6 +174,7 @@ class MediaStreamVideoCapturerSourceTest : public testing::Test { ...@@ -170,6 +174,7 @@ class MediaStreamVideoCapturerSourceTest : public testing::Test {
MockVideoCapturerSource* delegate_; // owned by |source_|. MockVideoCapturerSource* delegate_; // owned by |source_|.
blink::WebString webkit_source_id_; blink::WebString webkit_source_id_;
bool source_stopped_; bool source_stopped_;
bool stop_capture_flag_ = false;
}; };
TEST_F(MediaStreamVideoCapturerSourceTest, StartAndStop) { TEST_F(MediaStreamVideoCapturerSourceTest, StartAndStop) {
...@@ -318,4 +323,34 @@ TEST_F(MediaStreamVideoCapturerSourceTest, Restart) { ...@@ -318,4 +323,34 @@ TEST_F(MediaStreamVideoCapturerSourceTest, Restart) {
EXPECT_FALSE(source_->IsRunning()); EXPECT_FALSE(source_->IsRunning());
} }
TEST_F(MediaStreamVideoCapturerSourceTest, StartStopAndNotify) {
InSequence s;
EXPECT_CALL(mock_delegate(), MockStartCapture(_, _, _));
blink::WebMediaStreamTrack web_track =
StartSource(VideoTrackAdapterSettings(), base::nullopt, false, 0.0);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(blink::WebMediaStreamSource::kReadyStateLive,
webkit_source_.GetReadyState());
EXPECT_FALSE(source_stopped_);
stop_capture_flag_ = false;
EXPECT_CALL(mock_delegate(), MockStopCapture())
.WillOnce(InvokeWithoutArgs(
this, &MediaStreamVideoCapturerSourceTest::SetStopCaptureFlag));
EXPECT_CALL(*this, MockNotification());
MediaStreamTrack* track = MediaStreamTrack::GetTrack(web_track);
track->StopAndNotify(
base::BindOnce(&MediaStreamVideoCapturerSourceTest::MockNotification,
base::Unretained(this)));
EXPECT_EQ(blink::WebMediaStreamSource::kReadyStateEnded,
webkit_source_.GetReadyState());
EXPECT_TRUE(source_stopped_);
// It is a requirement that StopCapture() gets called in the same task as
// StopAndNotify(), as CORS security checks for element capture rely on this.
EXPECT_TRUE(stop_capture_flag_);
// The readyState is updated in the current task, but the notification is
// received on a separate task.
base::RunLoop().RunUntilIdle();
}
} // namespace content } // namespace content
...@@ -82,7 +82,8 @@ void MediaStreamVideoSource::AddTrack( ...@@ -82,7 +82,8 @@ void MediaStreamVideoSource::AddTrack(
} }
} }
void MediaStreamVideoSource::RemoveTrack(MediaStreamVideoTrack* video_track) { void MediaStreamVideoSource::RemoveTrack(MediaStreamVideoTrack* video_track,
base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::vector<MediaStreamVideoTrack*>::iterator it = std::vector<MediaStreamVideoTrack*>::iterator it =
std::find(tracks_.begin(), tracks_.end(), video_track); std::find(tracks_.begin(), tracks_.end(), video_track);
...@@ -101,8 +102,67 @@ void MediaStreamVideoSource::RemoveTrack(MediaStreamVideoTrack* video_track) { ...@@ -101,8 +102,67 @@ void MediaStreamVideoSource::RemoveTrack(MediaStreamVideoTrack* video_track) {
// failed and |frame_adapter_->AddCallback| has not been called. // failed and |frame_adapter_->AddCallback| has not been called.
track_adapter_->RemoveTrack(video_track); track_adapter_->RemoveTrack(video_track);
if (tracks_.empty()) if (tracks_.empty()) {
if (callback) {
// Using StopForRestart() in order to get a notification of when the
// source is actually stopped (if supported). The source will not be
// restarted.
// The intent is to have the same effect as StopSource() (i.e., having
// the readyState updated and invoking the source's stop callback on this
// task), but getting a notification of when the source has actually
// stopped so that clients have a mechanism to serialize the creation and
// destruction of video sources. Without such serialization it is possible
// that concurrent creation and destruction of sources that share the same
// underlying implementation results in failed source creation since
// stopping a source with StopSource() can have side effects that affect
// sources created after that StopSource() call, but before the actual
// stop takes place. See http://crbug.com/778039.
StopForRestart(base::BindOnce(&MediaStreamVideoSource::DidRemoveLastTrack,
weak_factory_.GetWeakPtr(),
std::move(callback)));
if (state_ == STOPPING_FOR_RESTART || state_ == STOPPED_FOR_RESTART) {
// If the source supports restarting, it is necessary to call
// FinalizeStopSource() to ensure the same behavior as StopSource(),
// even if the underlying implementation takes longer to actually stop.
// In particular, Tab capture and capture from element require the
// source's stop callback to be invoked on this task in order to ensure
// correct behavior.
FinalizeStopSource();
} else {
// If the source does not support restarting, call StopSource()
// to ensure stop on this task. DidRemoveLastTrack() will be called on
// another task even if the source does not support restarting, as
// StopForRestart() always posts a task to run its callback.
StopSource();
}
} else {
StopSource();
}
} else if (callback) {
std::move(callback).Run();
}
}
void MediaStreamVideoSource::DidRemoveLastTrack(base::OnceClosure callback,
RestartResult result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(callback);
DCHECK(tracks_.empty());
DCHECK_EQ(Owner().GetReadyState(),
blink::WebMediaStreamSource::kReadyStateEnded);
if (result == RestartResult::IS_STOPPED) {
state_ = ENDED;
}
if (state_ != ENDED) {
// This can happen if a source that supports StopForRestart() fails to
// actually stop the source after trying to stop it. The contract for
// StopForRestart() allows this, but it should not happen in practice.
LOG(WARNING) << "Source unexpectedly failed to stop. Force stopping and "
"sending notification anyway";
StopSource(); StopSource();
}
std::move(callback).Run();
} }
void MediaStreamVideoSource::ReconfigureTrack( void MediaStreamVideoSource::ReconfigureTrack(
...@@ -139,8 +199,9 @@ void MediaStreamVideoSource::StopSourceForRestartImpl() { ...@@ -139,8 +199,9 @@ void MediaStreamVideoSource::StopSourceForRestartImpl() {
void MediaStreamVideoSource::OnStopForRestartDone(bool did_stop_for_restart) { void MediaStreamVideoSource::OnStopForRestartDone(bool did_stop_for_restart) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (state_ == ENDED) if (state_ == ENDED) {
return; return;
}
DCHECK_EQ(state_, STOPPING_FOR_RESTART); DCHECK_EQ(state_, STOPPING_FOR_RESTART);
if (did_stop_for_restart) { if (did_stop_for_restart) {
......
...@@ -66,7 +66,7 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource { ...@@ -66,7 +66,7 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
const VideoTrackAdapterSettings& track_adapter_settings, const VideoTrackAdapterSettings& track_adapter_settings,
const VideoCaptureDeliverFrameCB& frame_callback, const VideoCaptureDeliverFrameCB& frame_callback,
const ConstraintsCallback& callback); const ConstraintsCallback& callback);
void RemoveTrack(MediaStreamVideoTrack* track); void RemoveTrack(MediaStreamVideoTrack* track, base::OnceClosure callback);
// Reconfigures this MediaStreamVideoSource to use |adapter_settings| on // Reconfigures this MediaStreamVideoSource to use |adapter_settings| on
// |track|, as long as |track| is connected to this source. // |track|, as long as |track| is connected to this source.
...@@ -253,6 +253,7 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource { ...@@ -253,6 +253,7 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
void StartFrameMonitoring(); void StartFrameMonitoring();
void UpdateTrackSettings(MediaStreamVideoTrack* track, void UpdateTrackSettings(MediaStreamVideoTrack* track,
const VideoTrackAdapterSettings& adapter_settings); const VideoTrackAdapterSettings& adapter_settings);
void DidRemoveLastTrack(base::OnceClosure callback, RestartResult result);
State state_; State state_;
......
...@@ -70,6 +70,8 @@ class MediaStreamVideoSourceTest : public ::testing::Test { ...@@ -70,6 +70,8 @@ class MediaStreamVideoSourceTest : public ::testing::Test {
blink::WebHeap::CollectAllGarbageForTesting(); blink::WebHeap::CollectAllGarbageForTesting();
} }
MOCK_METHOD0(MockNotification, void());
protected: protected:
MediaStreamVideoSource* source() { return mock_source_; } MediaStreamVideoSource* source() { return mock_source_; }
...@@ -637,4 +639,38 @@ TEST_F(MediaStreamVideoSourceTest, FailedRestartAfterStopForRestart) { ...@@ -637,4 +639,38 @@ TEST_F(MediaStreamVideoSourceTest, FailedRestartAfterStopForRestart) {
blink::WebMediaStreamSource::kReadyStateEnded); blink::WebMediaStreamSource::kReadyStateEnded);
} }
TEST_F(MediaStreamVideoSourceTest, StartStopAndNotifyRestartSupported) {
blink::WebMediaStreamTrack web_track = CreateTrack("123");
mock_source()->EnableStopForRestart();
mock_source()->StartMockedSource();
EXPECT_EQ(NumberOfSuccessConstraintsCallbacks(), 1);
EXPECT_EQ(web_track.Source().GetReadyState(),
blink::WebMediaStreamSource::kReadyStateLive);
EXPECT_CALL(*this, MockNotification());
MediaStreamTrack* track = MediaStreamTrack::GetTrack(web_track);
track->StopAndNotify(base::BindOnce(
&MediaStreamVideoSourceTest::MockNotification, base::Unretained(this)));
EXPECT_EQ(web_track.Source().GetReadyState(),
blink::WebMediaStreamSource::kReadyStateEnded);
base::RunLoop().RunUntilIdle();
}
TEST_F(MediaStreamVideoSourceTest, StartStopAndNotifyRestartNotSupported) {
blink::WebMediaStreamTrack web_track = CreateTrack("123");
mock_source()->DisableStopForRestart();
mock_source()->StartMockedSource();
EXPECT_EQ(NumberOfSuccessConstraintsCallbacks(), 1);
EXPECT_EQ(web_track.Source().GetReadyState(),
blink::WebMediaStreamSource::kReadyStateLive);
EXPECT_CALL(*this, MockNotification());
MediaStreamTrack* track = MediaStreamTrack::GetTrack(web_track);
track->StopAndNotify(base::BindOnce(
&MediaStreamVideoSourceTest::MockNotification, base::Unretained(this)));
EXPECT_EQ(web_track.Source().GetReadyState(),
blink::WebMediaStreamSource::kReadyStateEnded);
base::RunLoop().RunUntilIdle();
}
} // namespace content } // namespace content
...@@ -348,10 +348,10 @@ void MediaStreamVideoTrack::SetContentHint( ...@@ -348,10 +348,10 @@ void MediaStreamVideoTrack::SetContentHint(
sink->OnContentHintChanged(content_hint); sink->OnContentHintChanged(content_hint);
} }
void MediaStreamVideoTrack::Stop() { void MediaStreamVideoTrack::StopAndNotify(base::OnceClosure callback) {
DCHECK(main_render_thread_checker_.CalledOnValidThread()); DCHECK(main_render_thread_checker_.CalledOnValidThread());
if (source_) { if (source_) {
source_->RemoveTrack(this); source_->RemoveTrack(this, std::move(callback));
source_ = nullptr; source_ = nullptr;
} }
OnReadyStateChanged(blink::WebMediaStreamSource::kReadyStateEnded); OnReadyStateChanged(blink::WebMediaStreamSource::kReadyStateEnded);
......
...@@ -77,7 +77,7 @@ class CONTENT_EXPORT MediaStreamVideoTrack : public MediaStreamTrack { ...@@ -77,7 +77,7 @@ class CONTENT_EXPORT MediaStreamVideoTrack : public MediaStreamTrack {
void SetEnabled(bool enabled) override; void SetEnabled(bool enabled) override;
void SetContentHint( void SetContentHint(
blink::WebMediaStreamTrack::ContentHintType content_hint) override; blink::WebMediaStreamTrack::ContentHintType content_hint) override;
void Stop() override; void StopAndNotify(base::OnceClosure callback) override;
void GetSettings(blink::WebMediaStreamTrack::Settings& settings) override; void GetSettings(blink::WebMediaStreamTrack::Settings& settings) override;
void OnReadyStateChanged(blink::WebMediaStreamSource::ReadyState state); void OnReadyStateChanged(blink::WebMediaStreamSource::ReadyState state);
......
...@@ -57,6 +57,7 @@ UserMediaClientImpl::Request::Request(std::unique_ptr<UserMediaRequest> request) ...@@ -57,6 +57,7 @@ UserMediaClientImpl::Request::Request(std::unique_ptr<UserMediaRequest> request)
: user_media_request_(std::move(request)) { : user_media_request_(std::move(request)) {
DCHECK(user_media_request_); DCHECK(user_media_request_);
DCHECK(apply_constraints_request_.IsNull()); DCHECK(apply_constraints_request_.IsNull());
DCHECK(web_track_to_stop_.IsNull());
} }
UserMediaClientImpl::Request::Request( UserMediaClientImpl::Request::Request(
...@@ -64,12 +65,32 @@ UserMediaClientImpl::Request::Request( ...@@ -64,12 +65,32 @@ UserMediaClientImpl::Request::Request(
: apply_constraints_request_(request) { : apply_constraints_request_(request) {
DCHECK(!apply_constraints_request_.IsNull()); DCHECK(!apply_constraints_request_.IsNull());
DCHECK(!user_media_request_); DCHECK(!user_media_request_);
DCHECK(web_track_to_stop_.IsNull());
}
UserMediaClientImpl::Request::Request(
const blink::WebMediaStreamTrack& web_track_to_stop)
: web_track_to_stop_(web_track_to_stop) {
DCHECK(!web_track_to_stop_.IsNull());
DCHECK(!user_media_request_);
DCHECK(apply_constraints_request_.IsNull());
} }
UserMediaClientImpl::Request::Request(Request&& other) UserMediaClientImpl::Request::Request(Request&& other)
: user_media_request_(std::move(other.user_media_request_)), : user_media_request_(std::move(other.user_media_request_)),
apply_constraints_request_(other.apply_constraints_request_) { apply_constraints_request_(other.apply_constraints_request_),
DCHECK(!IsApplyConstraints() || !IsUserMedia()); web_track_to_stop_(other.web_track_to_stop_) {
#if DCHECK_IS_ON()
int num_types = 0;
if (IsUserMedia())
num_types++;
if (IsApplyConstraints())
num_types++;
if (IsStopTrack())
num_types++;
DCHECK_EQ(num_types, 1);
#endif
} }
UserMediaClientImpl::Request& UserMediaClientImpl::Request::operator=( UserMediaClientImpl::Request& UserMediaClientImpl::Request::operator=(
...@@ -176,6 +197,17 @@ void UserMediaClientImpl::ApplyConstraints( ...@@ -176,6 +197,17 @@ void UserMediaClientImpl::ApplyConstraints(
} }
} }
void UserMediaClientImpl::StopTrack(
const blink::WebMediaStreamTrack& web_track) {
pending_request_infos_.push_back(Request(web_track));
if (!is_processing_request_) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&UserMediaClientImpl::MaybeProcessNextRequestInfo,
weak_factory_.GetWeakPtr()));
}
}
void UserMediaClientImpl::MaybeProcessNextRequestInfo() { void UserMediaClientImpl::MaybeProcessNextRequestInfo() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (is_processing_request_ || pending_request_infos_.empty()) if (is_processing_request_ || pending_request_infos_.empty())
...@@ -192,12 +224,22 @@ void UserMediaClientImpl::MaybeProcessNextRequestInfo() { ...@@ -192,12 +224,22 @@ void UserMediaClientImpl::MaybeProcessNextRequestInfo() {
current_request.MoveUserMediaRequest(), current_request.MoveUserMediaRequest(),
base::BindOnce(&UserMediaClientImpl::CurrentRequestCompleted, base::BindOnce(&UserMediaClientImpl::CurrentRequestCompleted,
base::Unretained(this))); base::Unretained(this)));
} else { } else if (current_request.IsApplyConstraints()) {
DCHECK(current_request.IsApplyConstraints());
apply_constraints_processor_->ProcessRequest( apply_constraints_processor_->ProcessRequest(
current_request.apply_constraints_request(), current_request.apply_constraints_request(),
base::BindOnce(&UserMediaClientImpl::CurrentRequestCompleted, base::BindOnce(&UserMediaClientImpl::CurrentRequestCompleted,
base::Unretained(this))); base::Unretained(this)));
} else {
DCHECK(current_request.IsStopTrack());
MediaStreamTrack* track =
MediaStreamTrack::GetTrack(current_request.web_track_to_stop());
if (track) {
track->StopAndNotify(
base::BindOnce(&UserMediaClientImpl::CurrentRequestCompleted,
weak_factory_.GetWeakPtr()));
} else {
CurrentRequestCompleted();
}
} }
} }
......
...@@ -69,6 +69,7 @@ class CONTENT_EXPORT UserMediaClientImpl : public RenderFrameObserver, ...@@ -69,6 +69,7 @@ class CONTENT_EXPORT UserMediaClientImpl : public RenderFrameObserver,
const blink::WebMediaDeviceChangeObserver& observer) override; const blink::WebMediaDeviceChangeObserver& observer) override;
void ApplyConstraints( void ApplyConstraints(
const blink::WebApplyConstraintsRequest& web_request) override; const blink::WebApplyConstraintsRequest& web_request) override;
void StopTrack(const blink::WebMediaStreamTrack& web_track) override;
// RenderFrameObserver override // RenderFrameObserver override
void WillCommitProvisionalLoad() override; void WillCommitProvisionalLoad() override;
...@@ -89,6 +90,7 @@ class CONTENT_EXPORT UserMediaClientImpl : public RenderFrameObserver, ...@@ -89,6 +90,7 @@ class CONTENT_EXPORT UserMediaClientImpl : public RenderFrameObserver,
public: public:
explicit Request(std::unique_ptr<UserMediaRequest> request); explicit Request(std::unique_ptr<UserMediaRequest> request);
explicit Request(const blink::WebApplyConstraintsRequest& request); explicit Request(const blink::WebApplyConstraintsRequest& request);
explicit Request(const blink::WebMediaStreamTrack& request);
Request(Request&& other); Request(Request&& other);
Request& operator=(Request&& other); Request& operator=(Request&& other);
~Request(); ~Request();
...@@ -98,20 +100,23 @@ class CONTENT_EXPORT UserMediaClientImpl : public RenderFrameObserver, ...@@ -98,20 +100,23 @@ class CONTENT_EXPORT UserMediaClientImpl : public RenderFrameObserver,
UserMediaRequest* user_media_request() const { UserMediaRequest* user_media_request() const {
return user_media_request_.get(); return user_media_request_.get();
} }
const blink::WebApplyConstraintsRequest& apply_constraints_request() const { const blink::WebApplyConstraintsRequest& apply_constraints_request() const {
return apply_constraints_request_; return apply_constraints_request_;
} }
const blink::WebMediaStreamTrack& web_track_to_stop() const {
return web_track_to_stop_;
}
bool IsUserMedia() const { return !!user_media_request_; }
bool IsApplyConstraints() const { bool IsApplyConstraints() const {
return !apply_constraints_request_.IsNull(); return !apply_constraints_request_.IsNull();
} }
bool IsStopTrack() const { return !web_track_to_stop_.IsNull(); }
bool IsUserMedia() const { return !!user_media_request_; }
private: private:
std::unique_ptr<UserMediaRequest> user_media_request_; std::unique_ptr<UserMediaRequest> user_media_request_;
blink::WebApplyConstraintsRequest apply_constraints_request_; blink::WebApplyConstraintsRequest apply_constraints_request_;
blink::WebMediaStreamTrack web_track_to_stop_;
}; };
void MaybeProcessNextRequestInfo(); void MaybeProcessNextRequestInfo();
......
...@@ -869,6 +869,20 @@ ...@@ -869,6 +869,20 @@
} }
video.srcObject = canvas_stream; video.srcObject = canvas_stream;
} }
function concurrentGetUserMediaStop() {
var num_stopped = 0;
const N = 250;
for (var i = 0; i < N; i++) {
navigator.mediaDevices.getUserMedia({video: true}).then(s => {
setTimeout(() => {
s.getVideoTracks()[0].stop();
if (++num_stopped == N)
reportTestSuccess();
}, 1);
}).catch(e => failTest("getUserMedia failed: " + e.name));
}
}
</script> </script>
</head> </head>
<body> <body>
......
...@@ -61,7 +61,7 @@ void MediaElementEventListener::handleEvent(ExecutionContext* context, ...@@ -61,7 +61,7 @@ void MediaElementEventListener::handleEvent(ExecutionContext* context,
if (event->type() == EventTypeNames::ended) { if (event->type() == EventTypeNames::ended) {
const MediaStreamTrackVector tracks = media_stream_->getTracks(); const MediaStreamTrackVector tracks = media_stream_->getTracks();
for (const auto& track : tracks) { for (const auto& track : tracks) {
track->stopTrack(ASSERT_NO_EXCEPTION); track->stopTrack(context);
media_stream_->RemoveTrackByComponent(track->Component()); media_stream_->RemoveTrackByComponent(track->Component());
} }
...@@ -75,7 +75,7 @@ void MediaElementEventListener::handleEvent(ExecutionContext* context, ...@@ -75,7 +75,7 @@ void MediaElementEventListener::handleEvent(ExecutionContext* context,
if (media_element_->GetLoadType() == WebMediaPlayer::kLoadTypeMediaStream) { if (media_element_->GetLoadType() == WebMediaPlayer::kLoadTypeMediaStream) {
const MediaStreamTrackVector tracks = media_stream_->getTracks(); const MediaStreamTrackVector tracks = media_stream_->getTracks();
for (const auto& track : tracks) { for (const auto& track : tracks) {
track->stopTrack(ASSERT_NO_EXCEPTION); track->stopTrack(context);
media_stream_->RemoveTrackByComponent(track->Component()); media_stream_->RemoveTrackByComponent(track->Component());
} }
MediaStreamDescriptor* const descriptor = MediaStreamDescriptor* const descriptor =
......
...@@ -275,12 +275,17 @@ String MediaStreamTrack::readyState() const { ...@@ -275,12 +275,17 @@ String MediaStreamTrack::readyState() const {
return String(); return String();
} }
void MediaStreamTrack::stopTrack(ExceptionState& exception_state) { void MediaStreamTrack::stopTrack(ExecutionContext* execution_context) {
if (Ended()) if (Ended())
return; return;
ready_state_ = MediaStreamSource::kReadyStateEnded; ready_state_ = MediaStreamSource::kReadyStateEnded;
MediaStreamCenter::Instance().DidStopMediaStreamTrack(Component()); Document* document = ToDocument(execution_context);
UserMediaController* user_media =
UserMediaController::From(document->GetFrame());
if (user_media)
user_media->StopTrack(Component());
PropagateTrackEnded(); PropagateTrackEnded();
} }
......
...@@ -39,7 +39,6 @@ ...@@ -39,7 +39,6 @@
namespace blink { namespace blink {
class AudioSourceProvider; class AudioSourceProvider;
class ExceptionState;
class ImageCapture; class ImageCapture;
class MediaTrackCapabilities; class MediaTrackCapabilities;
class MediaTrackConstraints; class MediaTrackConstraints;
...@@ -74,7 +73,7 @@ class MODULES_EXPORT MediaStreamTrack ...@@ -74,7 +73,7 @@ class MODULES_EXPORT MediaStreamTrack
String readyState() const; String readyState() const;
void stopTrack(ExceptionState&); void stopTrack(ExecutionContext*);
virtual MediaStreamTrack* clone(ScriptState*); virtual MediaStreamTrack* clone(ScriptState*);
// This function is called when constrains have been successfully applied. // This function is called when constrains have been successfully applied.
......
...@@ -48,7 +48,7 @@ enum MediaStreamTrackState { ...@@ -48,7 +48,7 @@ enum MediaStreamTrackState {
attribute EventHandler onended; attribute EventHandler onended;
[CallWith=ScriptState] MediaStreamTrack clone(); [CallWith=ScriptState] MediaStreamTrack clone();
[ImplementedAs=stopTrack, RaisesException] void stop(); [ImplementedAs=stopTrack, CallWith=ExecutionContext] void stop();
MediaTrackCapabilities getCapabilities(); MediaTrackCapabilities getCapabilities();
MediaTrackConstraints getConstraints(); MediaTrackConstraints getConstraints();
......
...@@ -75,4 +75,10 @@ void UserMediaClient::ApplyConstraints(ApplyConstraintsRequest* request) { ...@@ -75,4 +75,10 @@ void UserMediaClient::ApplyConstraints(ApplyConstraintsRequest* request) {
} }
} }
void UserMediaClient::StopTrack(MediaStreamComponent* track) {
if (client_) {
client_->StopTrack(WebMediaStreamTrack(track));
}
}
} // namespace blink } // namespace blink
...@@ -44,6 +44,7 @@ class ApplyConstraintsRequest; ...@@ -44,6 +44,7 @@ class ApplyConstraintsRequest;
class LocalFrame; class LocalFrame;
class MediaDevices; class MediaDevices;
class MediaDevicesRequest; class MediaDevicesRequest;
class MediaStreamComponent;
class UserMediaRequest; class UserMediaRequest;
class WebUserMediaClient; class WebUserMediaClient;
...@@ -58,6 +59,7 @@ class MODULES_EXPORT UserMediaClient { ...@@ -58,6 +59,7 @@ class MODULES_EXPORT UserMediaClient {
void RequestMediaDevices(MediaDevicesRequest*); void RequestMediaDevices(MediaDevicesRequest*);
void SetMediaDeviceChangeObserver(MediaDevices*); void SetMediaDeviceChangeObserver(MediaDevices*);
void ApplyConstraints(ApplyConstraintsRequest*); void ApplyConstraints(ApplyConstraintsRequest*);
void StopTrack(MediaStreamComponent*);
private: private:
explicit UserMediaClient(WebUserMediaClient*); explicit UserMediaClient(WebUserMediaClient*);
......
...@@ -35,6 +35,7 @@ namespace blink { ...@@ -35,6 +35,7 @@ namespace blink {
class ApplyConstraintsRequest; class ApplyConstraintsRequest;
class MediaDevices; class MediaDevices;
class MediaDevicesRequest; class MediaDevicesRequest;
class MediaStreamComponent;
class UserMediaRequest; class UserMediaRequest;
class UserMediaController final class UserMediaController final
...@@ -53,6 +54,7 @@ class UserMediaController final ...@@ -53,6 +54,7 @@ class UserMediaController final
void RequestMediaDevices(MediaDevicesRequest*); void RequestMediaDevices(MediaDevicesRequest*);
void SetMediaDeviceChangeObserver(MediaDevices*); void SetMediaDeviceChangeObserver(MediaDevices*);
void ApplyConstraints(ApplyConstraintsRequest*); void ApplyConstraints(ApplyConstraintsRequest*);
void StopTrack(MediaStreamComponent*);
static const char* SupplementName(); static const char* SupplementName();
static UserMediaController* From(LocalFrame* frame) { static UserMediaController* From(LocalFrame* frame) {
...@@ -88,6 +90,10 @@ inline void UserMediaController::ApplyConstraints( ...@@ -88,6 +90,10 @@ inline void UserMediaController::ApplyConstraints(
client_->ApplyConstraints(request); client_->ApplyConstraints(request);
} }
inline void UserMediaController::StopTrack(MediaStreamComponent* track) {
client_->StopTrack(track);
}
} // namespace blink } // namespace blink
#endif // UserMediaController_h #endif // UserMediaController_h
...@@ -68,10 +68,6 @@ void MediaStreamCenter::DidSetMediaStreamTrackEnabled( ...@@ -68,10 +68,6 @@ void MediaStreamCenter::DidSetMediaStreamTrackEnabled(
} }
} }
bool MediaStreamCenter::DidStopMediaStreamTrack(MediaStreamComponent* track) {
return private_ && private_->DidStopMediaStreamTrack(track);
}
void MediaStreamCenter::DidCreateMediaStreamAndTracks( void MediaStreamCenter::DidCreateMediaStreamAndTracks(
MediaStreamDescriptor* stream) { MediaStreamDescriptor* stream) {
if (!private_) if (!private_)
......
...@@ -63,7 +63,6 @@ class PLATFORM_EXPORT MediaStreamCenter final ...@@ -63,7 +63,6 @@ class PLATFORM_EXPORT MediaStreamCenter final
MediaStreamComponent* clone); MediaStreamComponent* clone);
void DidSetMediaStreamTrackEnabled(MediaStreamComponent*); void DidSetMediaStreamTrackEnabled(MediaStreamComponent*);
void DidSetContentHint(MediaStreamComponent*); void DidSetContentHint(MediaStreamComponent*);
bool DidStopMediaStreamTrack(MediaStreamComponent*);
std::unique_ptr<AudioSourceProvider> CreateWebAudioSourceFromMediaStreamTrack( std::unique_ptr<AudioSourceProvider> CreateWebAudioSourceFromMediaStreamTrack(
MediaStreamComponent*); MediaStreamComponent*);
......
...@@ -49,9 +49,6 @@ class WebMediaStreamCenter { ...@@ -49,9 +49,6 @@ class WebMediaStreamCenter {
virtual void DidSetContentHint(const WebMediaStreamTrack&) {} virtual void DidSetContentHint(const WebMediaStreamTrack&) {}
virtual void DidEnableMediaStreamTrack(const WebMediaStreamTrack&) {} virtual void DidEnableMediaStreamTrack(const WebMediaStreamTrack&) {}
virtual void DidDisableMediaStreamTrack(const WebMediaStreamTrack&) {} virtual void DidDisableMediaStreamTrack(const WebMediaStreamTrack&) {}
virtual bool DidStopMediaStreamTrack(const WebMediaStreamTrack&) {
return false;
}
// Source functionality. // Source functionality.
virtual void DidStopMediaStreamSource(const WebMediaStreamSource&) {} virtual void DidStopMediaStreamSource(const WebMediaStreamSource&) {}
......
...@@ -35,6 +35,7 @@ namespace blink { ...@@ -35,6 +35,7 @@ namespace blink {
class WebApplyConstraintsRequest; class WebApplyConstraintsRequest;
class WebMediaDevicesRequest; class WebMediaDevicesRequest;
class WebMediaStreamTrack;
class WebUserMediaRequest; class WebUserMediaRequest;
class WebMediaDeviceChangeObserver; class WebMediaDeviceChangeObserver;
...@@ -48,6 +49,7 @@ class WebUserMediaClient { ...@@ -48,6 +49,7 @@ class WebUserMediaClient {
virtual void SetMediaDeviceChangeObserver( virtual void SetMediaDeviceChangeObserver(
const WebMediaDeviceChangeObserver&) = 0; const WebMediaDeviceChangeObserver&) = 0;
virtual void ApplyConstraints(const WebApplyConstraintsRequest&) = 0; virtual void ApplyConstraints(const WebApplyConstraintsRequest&) = 0;
virtual void StopTrack(const WebMediaStreamTrack&) = 0;
}; };
} // namespace blink } // namespace blink
......
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