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

Add restart functionality to MediaStreamVideoSource.

This CL adds new methods to support restarting a MediaStreamVideoSource.
By default, sources continue to be nonrestartable, but the
MediaStreamVideoCapturerSource does implement the restart capability.

This is a preparatory CL to support MediaStreamTrack.applyConstraints(),
which requires the ability to change the video format of a video source.

Bug: 763319
Change-Id: I91b868326f1fcc52a8146612e4cd0429774f82e1
Reviewed-on: https://chromium-review.googlesource.com/675687
Commit-Queue: Guido Urdaneta <guidou@chromium.org>
Reviewed-by: default avatarYuri Wiitala <miu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#504344}
parent 1c5f70e3
...@@ -56,7 +56,7 @@ class LocalVideoCapturerSource final : public media::VideoCapturerSource { ...@@ -56,7 +56,7 @@ class LocalVideoCapturerSource final : public media::VideoCapturerSource {
VideoCaptureImplManager* const manager_; VideoCaptureImplManager* const manager_;
const base::Closure release_device_cb_; base::Closure release_device_cb_;
// These two are valid between StartCapture() and StopCapture(). // These two are valid between StartCapture() and StopCapture().
// |running_call_back_| is run when capture is successfully started, and when // |running_call_back_| is run when capture is successfully started, and when
...@@ -128,7 +128,6 @@ void LocalVideoCapturerSource::StopCapture() { ...@@ -128,7 +128,6 @@ void LocalVideoCapturerSource::StopCapture() {
// Immediately make sure we don't provide more frames. // Immediately make sure we don't provide more frames.
if (!stop_capture_cb_.is_null()) if (!stop_capture_cb_.is_null())
base::ResetAndReturn(&stop_capture_cb_).Run(); base::ResetAndReturn(&stop_capture_cb_).Run();
running_callback_.Reset();
} }
void LocalVideoCapturerSource::OnStateUpdate(VideoCaptureState state) { void LocalVideoCapturerSource::OnStateUpdate(VideoCaptureState state) {
...@@ -144,7 +143,9 @@ void LocalVideoCapturerSource::OnStateUpdate(VideoCaptureState state) { ...@@ -144,7 +143,9 @@ void LocalVideoCapturerSource::OnStateUpdate(VideoCaptureState state) {
case VIDEO_CAPTURE_STATE_STOPPED: case VIDEO_CAPTURE_STATE_STOPPED:
case VIDEO_CAPTURE_STATE_ERROR: case VIDEO_CAPTURE_STATE_ERROR:
case VIDEO_CAPTURE_STATE_ENDED: case VIDEO_CAPTURE_STATE_ENDED:
base::ResetAndReturn(&running_callback_).Run(false); release_device_cb_.Run();
release_device_cb_ = manager_->UseDevice(session_id_);
running_callback_.Run(false);
break; break;
case VIDEO_CAPTURE_STATE_STARTING: case VIDEO_CAPTURE_STATE_STARTING:
...@@ -209,11 +210,12 @@ void MediaStreamVideoCapturerSource::OnCapturingLinkSecured(bool is_secure) { ...@@ -209,11 +210,12 @@ void MediaStreamVideoCapturerSource::OnCapturingLinkSecured(bool is_secure) {
void MediaStreamVideoCapturerSource::StartSourceImpl( void MediaStreamVideoCapturerSource::StartSourceImpl(
const VideoCaptureDeliverFrameCB& frame_callback) { const VideoCaptureDeliverFrameCB& frame_callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
is_capture_starting_ = true; state_ = STARTING;
frame_callback_ = frame_callback;
source_->StartCapture( source_->StartCapture(
capture_params_, frame_callback, capture_params_, frame_callback_,
base::Bind(&MediaStreamVideoCapturerSource::OnRunStateChanged, base::Bind(&MediaStreamVideoCapturerSource::OnRunStateChanged,
base::Unretained(this))); base::Unretained(this), capture_params_));
} }
void MediaStreamVideoCapturerSource::StopSourceImpl() { void MediaStreamVideoCapturerSource::StopSourceImpl() {
...@@ -221,6 +223,27 @@ void MediaStreamVideoCapturerSource::StopSourceImpl() { ...@@ -221,6 +223,27 @@ void MediaStreamVideoCapturerSource::StopSourceImpl() {
source_->StopCapture(); source_->StopCapture();
} }
void MediaStreamVideoCapturerSource::StopSourceForRestartImpl() {
if (state_ != STARTED) {
OnStopForRestartDone(false);
return;
}
state_ = STOPPING_FOR_RESTART;
source_->StopCapture();
}
void MediaStreamVideoCapturerSource::RestartSourceImpl(
const media::VideoCaptureFormat& new_format) {
DCHECK(new_format.IsValid());
media::VideoCaptureParams new_capture_params = capture_params_;
new_capture_params.requested_format = new_format;
state_ = RESTARTING;
source_->StartCapture(
new_capture_params, frame_callback_,
base::Bind(&MediaStreamVideoCapturerSource::OnRunStateChanged,
base::Unretained(this), new_capture_params));
}
base::Optional<media::VideoCaptureFormat> base::Optional<media::VideoCaptureFormat>
MediaStreamVideoCapturerSource::GetCurrentFormat() const { MediaStreamVideoCapturerSource::GetCurrentFormat() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
...@@ -233,14 +256,42 @@ MediaStreamVideoCapturerSource::GetCurrentCaptureParams() const { ...@@ -233,14 +256,42 @@ MediaStreamVideoCapturerSource::GetCurrentCaptureParams() const {
return capture_params_; return capture_params_;
} }
void MediaStreamVideoCapturerSource::OnRunStateChanged(bool is_running) { void MediaStreamVideoCapturerSource::OnRunStateChanged(
const media::VideoCaptureParams& new_capture_params,
bool is_running) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (is_capture_starting_) { switch (state_) {
OnStartDone(is_running ? MEDIA_DEVICE_OK case STARTING:
: MEDIA_DEVICE_TRACK_START_FAILURE); if (is_running) {
is_capture_starting_ = false; state_ = STARTED;
} else if (!is_running) { DCHECK(capture_params_ == new_capture_params);
StopSource(); OnStartDone(MEDIA_DEVICE_OK);
} else {
state_ = STOPPED;
OnStartDone(MEDIA_DEVICE_TRACK_START_FAILURE);
}
break;
case STARTED:
if (!is_running) {
state_ = STOPPED;
StopSource();
}
break;
case STOPPING_FOR_RESTART:
state_ = is_running ? STARTED : STOPPED;
OnStopForRestartDone(!is_running);
break;
case RESTARTING:
if (is_running) {
state_ = STARTED;
capture_params_ = new_capture_params;
} else {
state_ = STOPPED;
}
OnRestartDone(is_running);
break;
case STOPPED:
break;
} }
} }
......
...@@ -55,6 +55,8 @@ class CONTENT_EXPORT MediaStreamVideoCapturerSource ...@@ -55,6 +55,8 @@ class CONTENT_EXPORT MediaStreamVideoCapturerSource
void StartSourceImpl( void StartSourceImpl(
const VideoCaptureDeliverFrameCB& frame_callback) override; const VideoCaptureDeliverFrameCB& frame_callback) override;
void StopSourceImpl() override; void StopSourceImpl() override;
void StopSourceForRestartImpl() override;
void RestartSourceImpl(const media::VideoCaptureFormat& new_format) override;
base::Optional<media::VideoCaptureFormat> GetCurrentFormat() const override; base::Optional<media::VideoCaptureFormat> GetCurrentFormat() const override;
base::Optional<media::VideoCaptureParams> GetCurrentCaptureParams() base::Optional<media::VideoCaptureParams> GetCurrentCaptureParams()
const override; const override;
...@@ -63,7 +65,8 @@ class CONTENT_EXPORT MediaStreamVideoCapturerSource ...@@ -63,7 +65,8 @@ class CONTENT_EXPORT MediaStreamVideoCapturerSource
void OnDestruct() final {} void OnDestruct() final {}
// Method to bind as RunningCallback in VideoCapturerSource::StartCapture(). // Method to bind as RunningCallback in VideoCapturerSource::StartCapture().
void OnRunStateChanged(bool is_running); void OnRunStateChanged(const media::VideoCaptureParams& new_capture_params,
bool is_running);
const mojom::MediaStreamDispatcherHostPtr& GetMediaStreamDispatcherHost(); const mojom::MediaStreamDispatcherHostPtr& GetMediaStreamDispatcherHost();
...@@ -72,12 +75,11 @@ class CONTENT_EXPORT MediaStreamVideoCapturerSource ...@@ -72,12 +75,11 @@ class CONTENT_EXPORT MediaStreamVideoCapturerSource
// The source that provides video frames. // The source that provides video frames.
const std::unique_ptr<media::VideoCapturerSource> source_; const std::unique_ptr<media::VideoCapturerSource> source_;
// Indicates whether the capture is in starting. It is set to true by enum State { STARTING, STARTED, STOPPING_FOR_RESTART, RESTARTING, STOPPED };
// StartSourceImpl() when starting the capture, and is reset after starting State state_ = STOPPED;
// is completed.
bool is_capture_starting_ = false;
media::VideoCaptureParams capture_params_; media::VideoCaptureParams capture_params_;
VideoCaptureDeliverFrameCB frame_callback_;
DISALLOW_COPY_AND_ASSIGN(MediaStreamVideoCapturerSource); DISALLOW_COPY_AND_ASSIGN(MediaStreamVideoCapturerSource);
}; };
......
...@@ -33,11 +33,34 @@ class MockVideoCapturerSource : public media::VideoCapturerSource { ...@@ -33,11 +33,34 @@ class MockVideoCapturerSource : public media::VideoCapturerSource {
MOCK_METHOD0(RequestRefreshFrame, void()); MOCK_METHOD0(RequestRefreshFrame, void());
MOCK_METHOD0(GetPreferredFormats, media::VideoCaptureFormats()); MOCK_METHOD0(GetPreferredFormats, media::VideoCaptureFormats());
MOCK_METHOD3(StartCapture, MOCK_METHOD3(MockStartCapture,
void(const media::VideoCaptureParams& params, void(const media::VideoCaptureParams& params,
const VideoCaptureDeliverFrameCB& new_frame_callback, const VideoCaptureDeliverFrameCB& new_frame_callback,
const RunningCallback& running_callback)); const RunningCallback& running_callback));
MOCK_METHOD0(StopCapture, void()); MOCK_METHOD0(MockStopCapture, void());
void StartCapture(const media::VideoCaptureParams& params,
const VideoCaptureDeliverFrameCB& new_frame_callback,
const RunningCallback& running_callback) override {
running_cb_ = running_callback;
capture_params_ = params;
MockStartCapture(params, new_frame_callback, running_callback);
SetRunning(true);
}
void StopCapture() {
MockStopCapture();
SetRunning(false);
}
void SetRunning(bool is_running) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(running_cb_, is_running));
}
const media::VideoCaptureParams& capture_params() const {
return capture_params_;
}
private:
RunningCallback running_cb_;
media::VideoCaptureParams capture_params_;
}; };
class FakeMediaStreamVideoSink : public MediaStreamVideoSink { class FakeMediaStreamVideoSink : public MediaStreamVideoSink {
...@@ -126,7 +149,9 @@ class MediaStreamVideoCapturerSourceTest : public testing::Test { ...@@ -126,7 +149,9 @@ class MediaStreamVideoCapturerSourceTest : public testing::Test {
source_stopped_ = true; source_stopped_ = true;
EXPECT_EQ(source.Id(), webkit_source_id_); EXPECT_EQ(source.Id(), webkit_source_id_);
} }
void OnStarted(bool result) { source_->OnRunStateChanged(result); } void OnStarted(bool result) {
source_->OnRunStateChanged(delegate_->capture_params(), result);
}
protected: protected:
void OnConstraintsApplied(MediaStreamSource* source, void OnConstraintsApplied(MediaStreamSource* source,
...@@ -141,28 +166,33 @@ class MediaStreamVideoCapturerSourceTest : public testing::Test { ...@@ -141,28 +166,33 @@ class MediaStreamVideoCapturerSourceTest : public testing::Test {
blink::WebMediaStreamSource webkit_source_; blink::WebMediaStreamSource webkit_source_;
MockMojoMediaStreamDispatcherHost mock_dispatcher_host_; MockMojoMediaStreamDispatcherHost mock_dispatcher_host_;
MediaStreamVideoCapturerSource* source_; // owned by |webkit_source_|. MediaStreamVideoCapturerSource* source_; // owned by |webkit_source_|.
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_;
}; };
TEST_F(MediaStreamVideoCapturerSourceTest, StartAndStop) { TEST_F(MediaStreamVideoCapturerSourceTest, StartAndStop) {
InSequence s; InSequence s;
EXPECT_CALL(mock_delegate(), StartCapture(_, _, _)); EXPECT_CALL(mock_delegate(), MockStartCapture(_, _, _));
blink::WebMediaStreamTrack track = StartSource( blink::WebMediaStreamTrack track =
VideoTrackAdapterSettings(), base::Optional<bool>(), false, 0.0); StartSource(VideoTrackAdapterSettings(), base::nullopt, false, 0.0);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_EQ(blink::WebMediaStreamSource::kReadyStateLive,
webkit_source_.GetReadyState());
EXPECT_FALSE(source_stopped_);
OnStarted(true); // 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(); base::RunLoop().RunUntilIdle();
EXPECT_EQ(blink::WebMediaStreamSource::kReadyStateLive, EXPECT_EQ(blink::WebMediaStreamSource::kReadyStateLive,
webkit_source_.GetReadyState()); webkit_source_.GetReadyState());
EXPECT_FALSE(source_stopped_); EXPECT_FALSE(source_stopped_);
EXPECT_TRUE(source_->GetCurrentCaptureParams().has_value()); EXPECT_TRUE(source_->GetCurrentCaptureParams().has_value());
EXPECT_CALL(mock_delegate(), StopCapture()); // If the delegate stops, the source should stop.
OnStarted(false); EXPECT_CALL(mock_delegate(), MockStopCapture());
delegate_->SetRunning(false);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_EQ(blink::WebMediaStreamSource::kReadyStateEnded, EXPECT_EQ(blink::WebMediaStreamSource::kReadyStateEnded,
webkit_source_.GetReadyState()); webkit_source_.GetReadyState());
...@@ -175,13 +205,13 @@ TEST_F(MediaStreamVideoCapturerSourceTest, CaptureTimeAndMetadataPlumbing) { ...@@ -175,13 +205,13 @@ TEST_F(MediaStreamVideoCapturerSourceTest, CaptureTimeAndMetadataPlumbing) {
media::VideoCapturerSource::RunningCallback running_cb; media::VideoCapturerSource::RunningCallback running_cb;
InSequence s; InSequence s;
EXPECT_CALL(mock_delegate(), StartCapture(_, _, _)) EXPECT_CALL(mock_delegate(), MockStartCapture(_, _, _))
.WillOnce(testing::DoAll(testing::SaveArg<1>(&deliver_frame_cb), .WillOnce(testing::DoAll(testing::SaveArg<1>(&deliver_frame_cb),
testing::SaveArg<2>(&running_cb))); testing::SaveArg<2>(&running_cb)));
EXPECT_CALL(mock_delegate(), RequestRefreshFrame()); EXPECT_CALL(mock_delegate(), RequestRefreshFrame());
EXPECT_CALL(mock_delegate(), StopCapture()); EXPECT_CALL(mock_delegate(), MockStopCapture());
blink::WebMediaStreamTrack track = StartSource( blink::WebMediaStreamTrack track =
VideoTrackAdapterSettings(), base::Optional<bool>(), false, 0.0); StartSource(VideoTrackAdapterSettings(), base::nullopt, false, 0.0);
running_cb.Run(true); running_cb.Run(true);
base::RunLoop run_loop; base::RunLoop run_loop;
...@@ -208,4 +238,83 @@ TEST_F(MediaStreamVideoCapturerSourceTest, CaptureTimeAndMetadataPlumbing) { ...@@ -208,4 +238,83 @@ TEST_F(MediaStreamVideoCapturerSourceTest, CaptureTimeAndMetadataPlumbing) {
EXPECT_EQ(30.0, metadata_value); EXPECT_EQ(30.0, metadata_value);
} }
TEST_F(MediaStreamVideoCapturerSourceTest, Restart) {
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_);
EXPECT_CALL(mock_delegate(), MockStopCapture());
EXPECT_TRUE(source_->IsRunning());
source_->StopForRestart(
base::BindOnce([](MediaStreamVideoSource::RestartResult result) {
EXPECT_EQ(result, MediaStreamVideoSource::RestartResult::IS_STOPPED);
}));
base::RunLoop().RunUntilIdle();
// When the source has stopped for restart, the source is not considered
// stopped, even if the underlying delegate is not running anymore.
// MediaStreamSource::SourceStoppedCallback should not be triggered.
EXPECT_EQ(webkit_source_.GetReadyState(),
blink::WebMediaStreamSource::kReadyStateLive);
EXPECT_FALSE(source_stopped_);
EXPECT_FALSE(source_->IsRunning());
// A second StopForRestart() should fail with invalid state, since it only
// makes sense when the source is running. Existing ready state should remain
// the same.
EXPECT_FALSE(source_->IsRunning());
source_->StopForRestart(
base::BindOnce([](MediaStreamVideoSource::RestartResult result) {
EXPECT_EQ(result, MediaStreamVideoSource::RestartResult::INVALID_STATE);
}));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(webkit_source_.GetReadyState(),
blink::WebMediaStreamSource::kReadyStateLive);
EXPECT_FALSE(source_stopped_);
EXPECT_FALSE(source_->IsRunning());
// Restart the source. With the mock delegate, any video format will do.
EXPECT_CALL(mock_delegate(), MockStartCapture(_, _, _));
EXPECT_FALSE(source_->IsRunning());
source_->Restart(
media::VideoCaptureFormat(),
base::BindOnce([](MediaStreamVideoSource::RestartResult result) {
EXPECT_EQ(result, MediaStreamVideoSource::RestartResult::IS_RUNNING);
}));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(webkit_source_.GetReadyState(),
blink::WebMediaStreamSource::kReadyStateLive);
EXPECT_TRUE(source_->IsRunning());
// A second Restart() should fail with invalid state since Restart() is
// defined only when the source is stopped for restart. Existing ready state
// should remain the same.
EXPECT_TRUE(source_->IsRunning());
source_->Restart(
media::VideoCaptureFormat(),
base::BindOnce([](MediaStreamVideoSource::RestartResult result) {
EXPECT_EQ(result, MediaStreamVideoSource::RestartResult::INVALID_STATE);
}));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(webkit_source_.GetReadyState(),
blink::WebMediaStreamSource::kReadyStateLive);
EXPECT_TRUE(source_->IsRunning());
// An delegate stop should stop the source and change the track state to
// "ended".
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_);
EXPECT_FALSE(source_->IsRunning());
}
} // namespace content } // namespace content
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event.h"
#include "content/child/child_process.h" #include "content/child/child_process.h"
#include "content/public/common/content_features.h" #include "content/public/common/content_features.h"
...@@ -53,7 +54,7 @@ void MediaStreamVideoSource::AddTrack( ...@@ -53,7 +54,7 @@ void MediaStreamVideoSource::AddTrack(
tracks_.push_back(track); tracks_.push_back(track);
secure_tracker_.Add(track, true); secure_tracker_.Add(track, true);
track_descriptors_.push_back(TrackDescriptor( pending_tracks_.push_back(PendingTrackInfo(
track, frame_callback, track, frame_callback,
base::MakeUnique<VideoTrackAdapterSettings>(track_adapter_settings), base::MakeUnique<VideoTrackAdapterSettings>(track_adapter_settings),
callback)); callback));
...@@ -65,13 +66,18 @@ void MediaStreamVideoSource::AddTrack( ...@@ -65,13 +66,18 @@ void MediaStreamVideoSource::AddTrack(
base::Bind(&VideoTrackAdapter::DeliverFrameOnIO, track_adapter_)); base::Bind(&VideoTrackAdapter::DeliverFrameOnIO, track_adapter_));
break; break;
} }
case STARTING: { case STARTING:
case STOPPING_FOR_RESTART:
case STOPPED_FOR_RESTART:
case RESTARTING: {
// These cases are handled by OnStartDone(), OnStoppedForRestartDone()
// and OnRestartDone().
break; break;
} }
case ENDED: case ENDED:
case STARTED: { case STARTED: {
// Currently, reconfiguring the source is not supported. FinalizeAddPendingTracks();
FinalizeAddTrack(); break;
} }
} }
} }
...@@ -84,10 +90,9 @@ void MediaStreamVideoSource::RemoveTrack(MediaStreamVideoTrack* video_track) { ...@@ -84,10 +90,9 @@ void MediaStreamVideoSource::RemoveTrack(MediaStreamVideoTrack* video_track) {
tracks_.erase(it); tracks_.erase(it);
secure_tracker_.Remove(video_track); secure_tracker_.Remove(video_track);
for (std::vector<TrackDescriptor>::iterator it = track_descriptors_.begin(); for (auto it = pending_tracks_.begin(); it != pending_tracks_.end(); ++it) {
it != track_descriptors_.end(); ++it) {
if (it->track == video_track) { if (it->track == video_track) {
track_descriptors_.erase(it); pending_tracks_.erase(it);
break; break;
} }
} }
...@@ -110,6 +115,86 @@ void MediaStreamVideoSource::ReconfigureTrack( ...@@ -110,6 +115,86 @@ void MediaStreamVideoSource::ReconfigureTrack(
UpdateTrackSettings(track, adapter_settings); UpdateTrackSettings(track, adapter_settings);
} }
void MediaStreamVideoSource::StopForRestart(RestartCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (state_ != STARTED) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), RestartResult::INVALID_STATE));
return;
}
DCHECK(!restart_callback_);
track_adapter_->StopFrameMonitoring();
state_ = STOPPING_FOR_RESTART;
restart_callback_ = std::move(callback);
StopSourceForRestartImpl();
}
void MediaStreamVideoSource::StopSourceForRestartImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(state_, STOPPING_FOR_RESTART);
OnStopForRestartDone(false);
}
void MediaStreamVideoSource::OnStopForRestartDone(bool did_stop_for_restart) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(state_, STOPPING_FOR_RESTART);
if (did_stop_for_restart) {
state_ = STOPPED_FOR_RESTART;
} else {
state_ = STARTED;
StartFrameMonitoring();
FinalizeAddPendingTracks();
}
DCHECK(restart_callback_);
RestartResult result = did_stop_for_restart ? RestartResult::IS_STOPPED
: RestartResult::IS_RUNNING;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(restart_callback_), result));
}
void MediaStreamVideoSource::Restart(
const media::VideoCaptureFormat& new_format,
RestartCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (state_ != STOPPED_FOR_RESTART) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), RestartResult::INVALID_STATE));
return;
}
DCHECK(!restart_callback_);
state_ = RESTARTING;
restart_callback_ = std::move(callback);
RestartSourceImpl(new_format);
}
void MediaStreamVideoSource::RestartSourceImpl(
const media::VideoCaptureFormat& new_format) {
NOTREACHED();
}
void MediaStreamVideoSource::OnRestartDone(bool did_restart) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(state_, RESTARTING);
if (did_restart) {
state_ = STARTED;
StartFrameMonitoring();
FinalizeAddPendingTracks();
} else {
StopSource();
return;
}
RestartResult result =
did_restart ? RestartResult::IS_RUNNING : RestartResult::IS_STOPPED;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(restart_callback_), result));
}
void MediaStreamVideoSource::UpdateHasConsumers(MediaStreamVideoTrack* track, void MediaStreamVideoSource::UpdateHasConsumers(MediaStreamVideoTrack* track,
bool has_consumers) { bool has_consumers) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
...@@ -177,28 +262,28 @@ void MediaStreamVideoSource::OnStartDone(MediaStreamRequestResult result) { ...@@ -177,28 +262,28 @@ void MediaStreamVideoSource::OnStartDone(MediaStreamRequestResult result) {
StopSource(); StopSource();
} }
// This object can be deleted after calling FinalizeAddTrack. See comment in // This object can be deleted after calling FinalizeAddPendingTracks. See
// the header file. // comment in the header file.
FinalizeAddTrack(); FinalizeAddPendingTracks();
} }
void MediaStreamVideoSource::FinalizeAddTrack() { void MediaStreamVideoSource::FinalizeAddPendingTracks() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::vector<TrackDescriptor> track_descriptors; std::vector<PendingTrackInfo> pending_track_descriptors;
track_descriptors.swap(track_descriptors_); pending_track_descriptors.swap(pending_tracks_);
for (const auto& track : track_descriptors) { for (const auto& track_info : pending_track_descriptors) {
MediaStreamRequestResult result = MEDIA_DEVICE_OK; MediaStreamRequestResult result = MEDIA_DEVICE_OK;
if (state_ != STARTED) if (state_ != STARTED)
result = MEDIA_DEVICE_TRACK_START_FAILURE; result = MEDIA_DEVICE_TRACK_START_FAILURE;
if (result == MEDIA_DEVICE_OK) { if (result == MEDIA_DEVICE_OK) {
track_adapter_->AddTrack(track.track, track.frame_callback, track_adapter_->AddTrack(track_info.track, track_info.frame_callback,
*track.adapter_settings); *track_info.adapter_settings);
UpdateTrackSettings(track.track, *track.adapter_settings); UpdateTrackSettings(track_info.track, *track_info.adapter_settings);
} }
if (!track.callback.is_null()) if (!track_info.callback.is_null())
track.callback.Run(this, result, blink::WebString()); track_info.callback.Run(this, result, blink::WebString());
} }
} }
...@@ -250,7 +335,7 @@ void MediaStreamVideoSource::UpdateTrackSettings( ...@@ -250,7 +335,7 @@ void MediaStreamVideoSource::UpdateTrackSettings(
adapter_settings.max_frame_rate); adapter_settings.max_frame_rate);
} }
MediaStreamVideoSource::TrackDescriptor::TrackDescriptor( MediaStreamVideoSource::PendingTrackInfo::PendingTrackInfo(
MediaStreamVideoTrack* track, MediaStreamVideoTrack* track,
const VideoCaptureDeliverFrameCB& frame_callback, const VideoCaptureDeliverFrameCB& frame_callback,
std::unique_ptr<VideoTrackAdapterSettings> adapter_settings, std::unique_ptr<VideoTrackAdapterSettings> adapter_settings,
...@@ -260,13 +345,12 @@ MediaStreamVideoSource::TrackDescriptor::TrackDescriptor( ...@@ -260,13 +345,12 @@ MediaStreamVideoSource::TrackDescriptor::TrackDescriptor(
adapter_settings(std::move(adapter_settings)), adapter_settings(std::move(adapter_settings)),
callback(callback) {} callback(callback) {}
MediaStreamVideoSource::TrackDescriptor::TrackDescriptor( MediaStreamVideoSource::PendingTrackInfo::PendingTrackInfo(
TrackDescriptor&& other) = default; PendingTrackInfo&& other) = default;
MediaStreamVideoSource::TrackDescriptor& MediaStreamVideoSource::PendingTrackInfo&
MediaStreamVideoSource::TrackDescriptor::operator=( MediaStreamVideoSource::PendingTrackInfo::operator=(
MediaStreamVideoSource::TrackDescriptor&& other) = default; MediaStreamVideoSource::PendingTrackInfo&& other) = default;
MediaStreamVideoSource::TrackDescriptor::~TrackDescriptor() { MediaStreamVideoSource::PendingTrackInfo::~PendingTrackInfo() {}
}
} // namespace content } // namespace content
...@@ -49,6 +49,10 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource { ...@@ -49,6 +49,10 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
kUnknownFrameRate = 0, kUnknownFrameRate = 0,
}; };
enum class RestartResult { IS_RUNNING, IS_STOPPED, INVALID_STATE };
// RestartCallback is used for both the StopForRestart and Restart operations.
using RestartCallback = base::OnceCallback<void(RestartResult)>;
static constexpr double kDefaultAspectRatio = static constexpr double kDefaultAspectRatio =
static_cast<double>(kDefaultWidth) / static_cast<double>(kDefaultHeight); static_cast<double>(kDefaultWidth) / static_cast<double>(kDefaultHeight);
...@@ -74,6 +78,39 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource { ...@@ -74,6 +78,39 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
void ReconfigureTrack(MediaStreamVideoTrack* track, void ReconfigureTrack(MediaStreamVideoTrack* track,
const VideoTrackAdapterSettings& adapter_settings); const VideoTrackAdapterSettings& adapter_settings);
// Tries to temporarily stop this source so that it can be later restarted
// with a different video format. Unlike MediaStreamVideoSource::StopSource(),
// a temporary stop for restart does not change the ready state of the source.
// Once the attempt to temporarily stop the source is completed, |callback|
// is invoked with IS_STOPPED if the source actually stopped, or IS_RUNNING
// if the source did not stop and is still running.
// This method can only be called after a source has started. This can be
// verified by checking that the IsRunning() method returns true.
// Any attempt to invoke StopForRestart() before the source has started
// results in no action and |callback| invoked with INVALID_STATE.
void StopForRestart(RestartCallback callback);
// Tries to restart a source that was previously temporarily stopped using the
// supplied |new_format|. This method can be invoked only after a successful
// call to StopForRestart().
// Once the attempt to restart the source is completed, |callback| is invoked
// with IS_RUNNING if the source restarted and IS_STOPPED if the source
// remained stopped. Note that it is not guaranteed that the source actually
// restarts using |new_format| as its configuration. After a successful
// restart, the actual configured format for the source (if available) can be
// obtained with a call to GetCurrentFormat().
// Note also that, since frames are delivered on a different thread, it is
// possible that frames using the old format are delivered for a while after
// a successful restart. Code relying on Restart() cannot assume that new
// frames are guaranteed to arrive in the new format until the first frame in
// the new format is received.
// This method can only be called after a successful stop for restart (i.e.,
// after the callback passed to StopForRestart() is invoked with a value of
// IS_STOPPED). Any attempt to invoke Restart() when the source is not in this
// state results in no action and |callback| invoked with INVALID_STATE.
void Restart(const media::VideoCaptureFormat& new_format,
RestartCallback callback);
// Called by |track| to notify the source whether it has any paths to a // Called by |track| to notify the source whether it has any paths to a
// consuming endpoint. // consuming endpoint.
void UpdateHasConsumers(MediaStreamVideoTrack* track, bool has_consumers); void UpdateHasConsumers(MediaStreamVideoTrack* track, bool has_consumers);
...@@ -96,6 +133,8 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource { ...@@ -96,6 +133,8 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
virtual base::Optional<media::VideoCaptureParams> GetCurrentCaptureParams() virtual base::Optional<media::VideoCaptureParams> GetCurrentCaptureParams()
const; const;
bool IsRunning() const { return state_ == STARTED; }
base::WeakPtr<MediaStreamVideoSource> GetWeakPtr() { base::WeakPtr<MediaStreamVideoSource> GetWeakPtr() {
return weak_factory_.GetWeakPtr(); return weak_factory_.GetWeakPtr();
} }
...@@ -117,9 +156,53 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource { ...@@ -117,9 +156,53 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
const VideoCaptureDeliverFrameCB& frame_callback) = 0; const VideoCaptureDeliverFrameCB& frame_callback) = 0;
void OnStartDone(MediaStreamRequestResult result); void OnStartDone(MediaStreamRequestResult result);
// An implementation must immediately stop capture video frames and must not // A subclass that supports restart must override this method such that it
// call OnSupportedFormats after this method has been called. After this // immediately stop producing video frames after this method is called.
// method has been called, MediaStreamVideoSource may be deleted. // The stop is intended to be temporary and to be followed by a restart. Thus,
// connected tracks should not be disconnected or notified about the source no
// longer producing frames. Once the source is stopped, the implementation
// must invoke OnStopForRestartDone() with true. If the source cannot stop,
// OnStopForRestartDone() must invoked with false.
// It can be assumed that this method is invoked only when the source is
// running.
// Note that if this method is overridden, RestartSourceImpl() must also be
// overridden following the respective contract. Otherwise, behavior is
// undefined.
// The default implementation does not support restart and just calls
// OnStopForRestartDone() with false.
virtual void StopSourceForRestartImpl();
// This method should be called by implementations once an attempt to stop
// for restart using StopSourceForRestartImpl() is completed.
// |did_stop_for_restart| must true if the source is stopped and false if
// the source is running.
void OnStopForRestartDone(bool did_stop_for_restart);
// A subclass that supports restart must override this method such that it
// tries to start producing frames after this method is called. If successful,
// the source should return to the same state as if it was started normally
// and invoke OnRestartDone() with true. The implementation should preferably
// restart to produce frames with the format specified in |new_format|.
// However, if this is not possible, the implementation is allowed to restart
// using a different format. In this case OnRestartDone() should be invoked
// with true as well. If it is impossible to restart the source with any
// format, the source should remain stopped and OnRestartDone() should be
// invoked with false.
// This method can only be invoked when the source is temporarily stopped
// after a successful OnStopForRestartDone(). Otherwise behavior is undefined.
// Note that if this method is overridden, StopSourceForRestartImpl() must
// also be overridden following the respective contract. Otherwise, behavior
// is undefined.
virtual void RestartSourceImpl(const media::VideoCaptureFormat& new_format);
// This method should be called by implementations once an attempt to restart
// the source completes. |did_restart| must be true if the source is running
// and false if the source is stopped.
void OnRestartDone(bool did_restart);
// An implementation must immediately stop producing video frames after this
// method has been called. After this method has been called,
// MediaStreamVideoSource may be deleted.
virtual void StopSourceImpl() = 0; virtual void StopSourceImpl() = 0;
// Optionally overridden by subclasses to act on whether there are any // Optionally overridden by subclasses to act on whether there are any
...@@ -135,6 +218,9 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource { ...@@ -135,6 +218,9 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
enum State { enum State {
NEW, NEW,
STARTING, STARTING,
STOPPING_FOR_RESTART,
STOPPED_FOR_RESTART,
RESTARTING,
STARTED, STARTED,
ENDED ENDED
}; };
...@@ -150,21 +236,27 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource { ...@@ -150,21 +236,27 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
// in the context of the callback. If gUM fails, the implementation will // in the context of the callback. If gUM fails, the implementation will
// simply drop the references to the blink source and track which will lead // simply drop the references to the blink source and track which will lead
// to this object being deleted. // to this object being deleted.
void FinalizeAddTrack(); void FinalizeAddPendingTracks();
// Actually adds |track| to this source, provided the source has started.
void FinalizeAddTrack(MediaStreamVideoTrack* track,
const VideoCaptureDeliverFrameCB& frame_callback,
const VideoTrackAdapterSettings& adapter_settings);
void StartFrameMonitoring(); void StartFrameMonitoring();
void UpdateTrackSettings(MediaStreamVideoTrack* track, void UpdateTrackSettings(MediaStreamVideoTrack* track,
const VideoTrackAdapterSettings& adapter_settings); const VideoTrackAdapterSettings& adapter_settings);
State state_; State state_;
struct TrackDescriptor { struct PendingTrackInfo {
TrackDescriptor(MediaStreamVideoTrack* track, PendingTrackInfo(
const VideoCaptureDeliverFrameCB& frame_callback, MediaStreamVideoTrack* track,
std::unique_ptr<VideoTrackAdapterSettings> adapter_settings, const VideoCaptureDeliverFrameCB& frame_callback,
const ConstraintsCallback& callback); std::unique_ptr<VideoTrackAdapterSettings> adapter_settings,
TrackDescriptor(TrackDescriptor&& other); const ConstraintsCallback& callback);
TrackDescriptor& operator=(TrackDescriptor&& other); PendingTrackInfo(PendingTrackInfo&& other);
~TrackDescriptor(); PendingTrackInfo& operator=(PendingTrackInfo&& other);
~PendingTrackInfo();
MediaStreamVideoTrack* track; MediaStreamVideoTrack* track;
VideoCaptureDeliverFrameCB frame_callback; VideoCaptureDeliverFrameCB frame_callback;
...@@ -173,7 +265,12 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource { ...@@ -173,7 +265,12 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
std::unique_ptr<VideoTrackAdapterSettings> adapter_settings; std::unique_ptr<VideoTrackAdapterSettings> adapter_settings;
ConstraintsCallback callback; ConstraintsCallback callback;
}; };
std::vector<TrackDescriptor> track_descriptors_; std::vector<PendingTrackInfo> pending_tracks_;
// |restart_callback_| is used for notifying both StopForRestart and Restart,
// since it is impossible to have a situation where there can be callbacks
// for both at the same time.
RestartCallback restart_callback_;
// |track_adapter_| delivers video frames to the tracks on the IO-thread. // |track_adapter_| delivers video frames to the tracks on the IO-thread.
const scoped_refptr<VideoTrackAdapter> track_adapter_; const scoped_refptr<VideoTrackAdapter> track_adapter_;
......
...@@ -503,4 +503,41 @@ TEST_F(MediaStreamVideoSourceTest, ReconfigureStoppedTrack) { ...@@ -503,4 +503,41 @@ TEST_F(MediaStreamVideoSourceTest, ReconfigureStoppedTrack) {
EXPECT_EQ(stopped_settings.aspect_ratio, -1); EXPECT_EQ(stopped_settings.aspect_ratio, -1);
} }
// Test that restart fails on a source without restart support.
TEST_F(MediaStreamVideoSourceTest, FailedRestart) {
blink::WebMediaStreamTrack track = CreateTrack("123");
mock_source()->StartMockedSource();
EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
EXPECT_EQ(track.Source().GetReadyState(),
blink::WebMediaStreamSource::kReadyStateLive);
// The source does not support Restart/StopForRestart.
mock_source()->StopForRestart(
base::BindOnce([](MediaStreamVideoSource::RestartResult result) {
EXPECT_EQ(result, MediaStreamVideoSource::RestartResult::IS_RUNNING);
}));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(track.Source().GetReadyState(),
blink::WebMediaStreamSource::kReadyStateLive);
// Verify that Restart() fails with INVALID_STATE when not called after a
// successful StopForRestart().
mock_source()->Restart(
media::VideoCaptureFormat(),
base::BindOnce([](MediaStreamVideoSource::RestartResult result) {
EXPECT_EQ(result, MediaStreamVideoSource::RestartResult::INVALID_STATE);
}));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(track.Source().GetReadyState(),
blink::WebMediaStreamSource::kReadyStateLive);
mock_source()->StopSource();
// Verify that StopForRestart() fails with INVALID_STATE when called when the
// source is not running.
mock_source()->StopForRestart(
base::BindOnce([](MediaStreamVideoSource::RestartResult result) {
EXPECT_EQ(result, MediaStreamVideoSource::RestartResult::INVALID_STATE);
}));
}
} // namespace content } // namespace content
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