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 {
VideoCaptureImplManager* const manager_;
const base::Closure release_device_cb_;
base::Closure release_device_cb_;
// These two are valid between StartCapture() and StopCapture().
// |running_call_back_| is run when capture is successfully started, and when
......@@ -128,7 +128,6 @@ void LocalVideoCapturerSource::StopCapture() {
// Immediately make sure we don't provide more frames.
if (!stop_capture_cb_.is_null())
base::ResetAndReturn(&stop_capture_cb_).Run();
running_callback_.Reset();
}
void LocalVideoCapturerSource::OnStateUpdate(VideoCaptureState state) {
......@@ -144,7 +143,9 @@ void LocalVideoCapturerSource::OnStateUpdate(VideoCaptureState state) {
case VIDEO_CAPTURE_STATE_STOPPED:
case VIDEO_CAPTURE_STATE_ERROR:
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;
case VIDEO_CAPTURE_STATE_STARTING:
......@@ -209,11 +210,12 @@ void MediaStreamVideoCapturerSource::OnCapturingLinkSecured(bool is_secure) {
void MediaStreamVideoCapturerSource::StartSourceImpl(
const VideoCaptureDeliverFrameCB& frame_callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
is_capture_starting_ = true;
state_ = STARTING;
frame_callback_ = frame_callback;
source_->StartCapture(
capture_params_, frame_callback,
capture_params_, frame_callback_,
base::Bind(&MediaStreamVideoCapturerSource::OnRunStateChanged,
base::Unretained(this)));
base::Unretained(this), capture_params_));
}
void MediaStreamVideoCapturerSource::StopSourceImpl() {
......@@ -221,6 +223,27 @@ void MediaStreamVideoCapturerSource::StopSourceImpl() {
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>
MediaStreamVideoCapturerSource::GetCurrentFormat() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......@@ -233,14 +256,42 @@ MediaStreamVideoCapturerSource::GetCurrentCaptureParams() const {
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_);
if (is_capture_starting_) {
OnStartDone(is_running ? MEDIA_DEVICE_OK
: MEDIA_DEVICE_TRACK_START_FAILURE);
is_capture_starting_ = false;
} else if (!is_running) {
StopSource();
switch (state_) {
case STARTING:
if (is_running) {
state_ = STARTED;
DCHECK(capture_params_ == new_capture_params);
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
void StartSourceImpl(
const VideoCaptureDeliverFrameCB& frame_callback) 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::VideoCaptureParams> GetCurrentCaptureParams()
const override;
......@@ -63,7 +65,8 @@ class CONTENT_EXPORT MediaStreamVideoCapturerSource
void OnDestruct() final {}
// 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();
......@@ -72,12 +75,11 @@ class CONTENT_EXPORT MediaStreamVideoCapturerSource
// The source that provides video frames.
const std::unique_ptr<media::VideoCapturerSource> source_;
// Indicates whether the capture is in starting. It is set to true by
// StartSourceImpl() when starting the capture, and is reset after starting
// is completed.
bool is_capture_starting_ = false;
enum State { STARTING, STARTED, STOPPING_FOR_RESTART, RESTARTING, STOPPED };
State state_ = STOPPED;
media::VideoCaptureParams capture_params_;
VideoCaptureDeliverFrameCB frame_callback_;
DISALLOW_COPY_AND_ASSIGN(MediaStreamVideoCapturerSource);
};
......
......@@ -33,11 +33,34 @@ class MockVideoCapturerSource : public media::VideoCapturerSource {
MOCK_METHOD0(RequestRefreshFrame, void());
MOCK_METHOD0(GetPreferredFormats, media::VideoCaptureFormats());
MOCK_METHOD3(StartCapture,
MOCK_METHOD3(MockStartCapture,
void(const media::VideoCaptureParams& params,
const VideoCaptureDeliverFrameCB& new_frame_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 {
......@@ -126,7 +149,9 @@ class MediaStreamVideoCapturerSourceTest : public testing::Test {
source_stopped_ = true;
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:
void OnConstraintsApplied(MediaStreamSource* source,
......@@ -141,28 +166,33 @@ class MediaStreamVideoCapturerSourceTest : public testing::Test {
blink::WebMediaStreamSource webkit_source_;
MockMojoMediaStreamDispatcherHost mock_dispatcher_host_;
MediaStreamVideoCapturerSource* source_; // owned by |webkit_source_|.
MockVideoCapturerSource* delegate_; // owned by |source|.
MockVideoCapturerSource* delegate_; // owned by |source_|.
blink::WebString webkit_source_id_;
bool source_stopped_;
};
TEST_F(MediaStreamVideoCapturerSourceTest, StartAndStop) {
InSequence s;
EXPECT_CALL(mock_delegate(), StartCapture(_, _, _));
blink::WebMediaStreamTrack track = StartSource(
VideoTrackAdapterSettings(), base::Optional<bool>(), false, 0.0);
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_);
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();
EXPECT_EQ(blink::WebMediaStreamSource::kReadyStateLive,
webkit_source_.GetReadyState());
EXPECT_FALSE(source_stopped_);
EXPECT_TRUE(source_->GetCurrentCaptureParams().has_value());
EXPECT_CALL(mock_delegate(), StopCapture());
OnStarted(false);
// 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());
......@@ -175,13 +205,13 @@ TEST_F(MediaStreamVideoCapturerSourceTest, CaptureTimeAndMetadataPlumbing) {
media::VideoCapturerSource::RunningCallback running_cb;
InSequence s;
EXPECT_CALL(mock_delegate(), StartCapture(_, _, _))
EXPECT_CALL(mock_delegate(), MockStartCapture(_, _, _))
.WillOnce(testing::DoAll(testing::SaveArg<1>(&deliver_frame_cb),
testing::SaveArg<2>(&running_cb)));
EXPECT_CALL(mock_delegate(), RequestRefreshFrame());
EXPECT_CALL(mock_delegate(), StopCapture());
blink::WebMediaStreamTrack track = StartSource(
VideoTrackAdapterSettings(), base::Optional<bool>(), false, 0.0);
EXPECT_CALL(mock_delegate(), MockStopCapture());
blink::WebMediaStreamTrack track =
StartSource(VideoTrackAdapterSettings(), base::nullopt, false, 0.0);
running_cb.Run(true);
base::RunLoop run_loop;
......@@ -208,4 +238,83 @@ TEST_F(MediaStreamVideoCapturerSourceTest, CaptureTimeAndMetadataPlumbing) {
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
......@@ -14,6 +14,7 @@
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "content/child/child_process.h"
#include "content/public/common/content_features.h"
......@@ -53,7 +54,7 @@ void MediaStreamVideoSource::AddTrack(
tracks_.push_back(track);
secure_tracker_.Add(track, true);
track_descriptors_.push_back(TrackDescriptor(
pending_tracks_.push_back(PendingTrackInfo(
track, frame_callback,
base::MakeUnique<VideoTrackAdapterSettings>(track_adapter_settings),
callback));
......@@ -65,13 +66,18 @@ void MediaStreamVideoSource::AddTrack(
base::Bind(&VideoTrackAdapter::DeliverFrameOnIO, track_adapter_));
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;
}
case ENDED:
case STARTED: {
// Currently, reconfiguring the source is not supported.
FinalizeAddTrack();
FinalizeAddPendingTracks();
break;
}
}
}
......@@ -84,10 +90,9 @@ void MediaStreamVideoSource::RemoveTrack(MediaStreamVideoTrack* video_track) {
tracks_.erase(it);
secure_tracker_.Remove(video_track);
for (std::vector<TrackDescriptor>::iterator it = track_descriptors_.begin();
it != track_descriptors_.end(); ++it) {
for (auto it = pending_tracks_.begin(); it != pending_tracks_.end(); ++it) {
if (it->track == video_track) {
track_descriptors_.erase(it);
pending_tracks_.erase(it);
break;
}
}
......@@ -110,6 +115,86 @@ void MediaStreamVideoSource::ReconfigureTrack(
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,
bool has_consumers) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......@@ -177,28 +262,28 @@ void MediaStreamVideoSource::OnStartDone(MediaStreamRequestResult result) {
StopSource();
}
// This object can be deleted after calling FinalizeAddTrack. See comment in
// the header file.
FinalizeAddTrack();
// This object can be deleted after calling FinalizeAddPendingTracks. See
// comment in the header file.
FinalizeAddPendingTracks();
}
void MediaStreamVideoSource::FinalizeAddTrack() {
void MediaStreamVideoSource::FinalizeAddPendingTracks() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::vector<TrackDescriptor> track_descriptors;
track_descriptors.swap(track_descriptors_);
for (const auto& track : track_descriptors) {
std::vector<PendingTrackInfo> pending_track_descriptors;
pending_track_descriptors.swap(pending_tracks_);
for (const auto& track_info : pending_track_descriptors) {
MediaStreamRequestResult result = MEDIA_DEVICE_OK;
if (state_ != STARTED)
result = MEDIA_DEVICE_TRACK_START_FAILURE;
if (result == MEDIA_DEVICE_OK) {
track_adapter_->AddTrack(track.track, track.frame_callback,
*track.adapter_settings);
UpdateTrackSettings(track.track, *track.adapter_settings);
track_adapter_->AddTrack(track_info.track, track_info.frame_callback,
*track_info.adapter_settings);
UpdateTrackSettings(track_info.track, *track_info.adapter_settings);
}
if (!track.callback.is_null())
track.callback.Run(this, result, blink::WebString());
if (!track_info.callback.is_null())
track_info.callback.Run(this, result, blink::WebString());
}
}
......@@ -250,7 +335,7 @@ void MediaStreamVideoSource::UpdateTrackSettings(
adapter_settings.max_frame_rate);
}
MediaStreamVideoSource::TrackDescriptor::TrackDescriptor(
MediaStreamVideoSource::PendingTrackInfo::PendingTrackInfo(
MediaStreamVideoTrack* track,
const VideoCaptureDeliverFrameCB& frame_callback,
std::unique_ptr<VideoTrackAdapterSettings> adapter_settings,
......@@ -260,13 +345,12 @@ MediaStreamVideoSource::TrackDescriptor::TrackDescriptor(
adapter_settings(std::move(adapter_settings)),
callback(callback) {}
MediaStreamVideoSource::TrackDescriptor::TrackDescriptor(
TrackDescriptor&& other) = default;
MediaStreamVideoSource::TrackDescriptor&
MediaStreamVideoSource::TrackDescriptor::operator=(
MediaStreamVideoSource::TrackDescriptor&& other) = default;
MediaStreamVideoSource::PendingTrackInfo::PendingTrackInfo(
PendingTrackInfo&& other) = default;
MediaStreamVideoSource::PendingTrackInfo&
MediaStreamVideoSource::PendingTrackInfo::operator=(
MediaStreamVideoSource::PendingTrackInfo&& other) = default;
MediaStreamVideoSource::TrackDescriptor::~TrackDescriptor() {
}
MediaStreamVideoSource::PendingTrackInfo::~PendingTrackInfo() {}
} // namespace content
......@@ -49,6 +49,10 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
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_cast<double>(kDefaultWidth) / static_cast<double>(kDefaultHeight);
......@@ -74,6 +78,39 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
void ReconfigureTrack(MediaStreamVideoTrack* track,
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
// consuming endpoint.
void UpdateHasConsumers(MediaStreamVideoTrack* track, bool has_consumers);
......@@ -96,6 +133,8 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
virtual base::Optional<media::VideoCaptureParams> GetCurrentCaptureParams()
const;
bool IsRunning() const { return state_ == STARTED; }
base::WeakPtr<MediaStreamVideoSource> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
......@@ -117,9 +156,53 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
const VideoCaptureDeliverFrameCB& frame_callback) = 0;
void OnStartDone(MediaStreamRequestResult result);
// An implementation must immediately stop capture video frames and must not
// call OnSupportedFormats after this method has been called. After this
// method has been called, MediaStreamVideoSource may be deleted.
// A subclass that supports restart must override this method such that it
// immediately stop producing video frames after this method is called.
// 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;
// Optionally overridden by subclasses to act on whether there are any
......@@ -135,6 +218,9 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
enum State {
NEW,
STARTING,
STOPPING_FOR_RESTART,
STOPPED_FOR_RESTART,
RESTARTING,
STARTED,
ENDED
};
......@@ -150,21 +236,27 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
// 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
// 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 UpdateTrackSettings(MediaStreamVideoTrack* track,
const VideoTrackAdapterSettings& adapter_settings);
State state_;
struct TrackDescriptor {
TrackDescriptor(MediaStreamVideoTrack* track,
const VideoCaptureDeliverFrameCB& frame_callback,
std::unique_ptr<VideoTrackAdapterSettings> adapter_settings,
const ConstraintsCallback& callback);
TrackDescriptor(TrackDescriptor&& other);
TrackDescriptor& operator=(TrackDescriptor&& other);
~TrackDescriptor();
struct PendingTrackInfo {
PendingTrackInfo(
MediaStreamVideoTrack* track,
const VideoCaptureDeliverFrameCB& frame_callback,
std::unique_ptr<VideoTrackAdapterSettings> adapter_settings,
const ConstraintsCallback& callback);
PendingTrackInfo(PendingTrackInfo&& other);
PendingTrackInfo& operator=(PendingTrackInfo&& other);
~PendingTrackInfo();
MediaStreamVideoTrack* track;
VideoCaptureDeliverFrameCB frame_callback;
......@@ -173,7 +265,12 @@ class CONTENT_EXPORT MediaStreamVideoSource : public MediaStreamSource {
std::unique_ptr<VideoTrackAdapterSettings> adapter_settings;
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.
const scoped_refptr<VideoTrackAdapter> track_adapter_;
......
......@@ -503,4 +503,41 @@ TEST_F(MediaStreamVideoSourceTest, ReconfigureStoppedTrack) {
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
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