From 251e84deb09ec711daa229c36e1fe19ac0d318d0 Mon Sep 17 00:00:00 2001 From: "mcasas@chromium.org" Date: Wed, 16 Jul 2014 00:02:00 +0000 Subject: [PATCH] VideoTrackAdapter: Add passing frames monitor, notify MSVCS -> MSVTrack(s) MediaStreamVideoSource owns a VideoTrackAdapter, that sees the frames passing by (possibly adapting frame rate and resolution). This CL extends this VTA to monitor passing frames. Every time the monitoring wakes up, it notifies the MediaStreamVideoSource of the muted state via SetMutedState. This Class has no state, simply updates all registered MediaStreamVideoTracks, who have a muted state. Later CLs will further connect the MSVT::SetMutedState() to ping WebMediaStreamTrack etc. BUG=389159 Review URL: https://codereview.chromium.org/366243003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@283285 0039d316-1c4b-4281-b951-d872f2087c98 --- content/renderer/media/media_stream_track.cc | 16 +++- content/renderer/media/media_stream_track.h | 9 +++ .../media/media_stream_video_capturer_source.cc | 4 +- .../media/media_stream_video_capturer_source.h | 1 - .../renderer/media/media_stream_video_source.cc | 24 +++++- content/renderer/media/media_stream_video_source.h | 10 ++- .../media/media_stream_video_source_unittest.cc | 87 ++++++++++++++++++++++ content/renderer/media/media_stream_video_track.cc | 11 ++- content/renderer/media/media_stream_video_track.h | 3 + .../media/mock_media_stream_video_source.h | 9 +++ content/renderer/media/video_track_adapter.cc | 71 ++++++++++++++++-- content/renderer/media/video_track_adapter.h | 23 +++++- 12 files changed, 249 insertions(+), 19 deletions(-) diff --git a/content/renderer/media/media_stream_track.cc b/content/renderer/media/media_stream_track.cc index 529bc384e894..9aca963af4f4 100644 --- a/content/renderer/media/media_stream_track.cc +++ b/content/renderer/media/media_stream_track.cc @@ -20,18 +20,31 @@ MediaStreamTrack* MediaStreamTrack::GetTrack( MediaStreamTrack::MediaStreamTrack( webrtc::MediaStreamTrackInterface* track, bool is_local_track) : track_(track), - is_local_track_(is_local_track) { + muted_state_(false), + is_local_track_(is_local_track){ } MediaStreamTrack::~MediaStreamTrack() { } void MediaStreamTrack::SetEnabled(bool enabled) { + DCHECK(thread_checker_.CalledOnValidThread()); if (track_) track_->set_enabled(enabled); } +void MediaStreamTrack::SetMutedState(bool muted_state) { + DCHECK(thread_checker_.CalledOnValidThread()); + muted_state_ = muted_state; +} + +bool MediaStreamTrack::GetMutedState(void) const { + DCHECK(thread_checker_.CalledOnValidThread()); + return muted_state_; +} + void MediaStreamTrack::Stop() { + DCHECK(thread_checker_.CalledOnValidThread()); // Stop means that a track should be stopped permanently. But // since there is no proper way of doing that on a remote track, we can // at least disable the track. Blink will not call down to the content layer @@ -41,6 +54,7 @@ void MediaStreamTrack::Stop() { } webrtc::AudioTrackInterface* MediaStreamTrack::GetAudioAdapter() { + DCHECK(thread_checker_.CalledOnValidThread()); return static_cast(track_.get()); } diff --git a/content/renderer/media/media_stream_track.h b/content/renderer/media/media_stream_track.h index 456005d4d054..a941b59b62e4 100644 --- a/content/renderer/media/media_stream_track.h +++ b/content/renderer/media/media_stream_track.h @@ -8,6 +8,7 @@ #include "base/callback.h" #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" +#include "base/threading/thread_checker.h" #include "content/common/content_export.h" #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" @@ -34,6 +35,9 @@ class CONTENT_EXPORT MediaStreamTrack // If a subclass overrides this method it has to call the base class. virtual void SetEnabled(bool enabled); + virtual void SetMutedState(bool muted_state); + virtual bool GetMutedState(void) const; + // TODO(xians): Make this pure virtual when Stop[Track] has been // implemented for remote audio tracks. virtual void Stop(); @@ -45,9 +49,14 @@ class CONTENT_EXPORT MediaStreamTrack protected: scoped_refptr track_; + // Set to true if the owner MediaStreamSource is not delivering frames. + bool muted_state_; + private: const bool is_local_track_; + base::ThreadChecker thread_checker_; + DISALLOW_COPY_AND_ASSIGN(MediaStreamTrack); }; diff --git a/content/renderer/media/media_stream_video_capturer_source.cc b/content/renderer/media/media_stream_video_capturer_source.cc index f4555afb4f01..1266853362bb 100644 --- a/content/renderer/media/media_stream_video_capturer_source.cc +++ b/content/renderer/media/media_stream_video_capturer_source.cc @@ -38,8 +38,7 @@ VideoCapturerDelegate::VideoCapturerDelegate( const StreamDeviceInfo& device_info) : session_id_(device_info.session_id), is_screen_cast_(device_info.device.type == MEDIA_TAB_VIDEO_CAPTURE || - device_info.device.type == MEDIA_DESKTOP_VIDEO_CAPTURE), - got_first_frame_(false) { + device_info.device.type == MEDIA_DESKTOP_VIDEO_CAPTURE) { DVLOG(3) << "VideoCapturerDelegate::ctor"; // NULL in unit test. @@ -103,7 +102,6 @@ void VideoCapturerDelegate::StartCapture( DCHECK(params.requested_format.IsValid()); DCHECK(thread_checker_.CalledOnValidThread()); running_callback_ = running_callback; - got_first_frame_ = false; // NULL in unit test. if (!RenderThreadImpl::current()) diff --git a/content/renderer/media/media_stream_video_capturer_source.h b/content/renderer/media/media_stream_video_capturer_source.h index dcffa02d371a..3b9588aa799c 100644 --- a/content/renderer/media/media_stream_video_capturer_source.h +++ b/content/renderer/media/media_stream_video_capturer_source.h @@ -70,7 +70,6 @@ class CONTENT_EXPORT VideoCapturerDelegate base::Closure stop_capture_cb_; bool is_screen_cast_; - bool got_first_frame_; // |running_callback| is provided to this class in StartCapture and must be // valid until StopCapture is called. diff --git a/content/renderer/media/media_stream_video_source.cc b/content/renderer/media/media_stream_video_source.cc index 45c6abd7aa0f..7191c27d7ba2 100644 --- a/content/renderer/media/media_stream_video_source.cc +++ b/content/renderer/media/media_stream_video_source.cc @@ -15,6 +15,7 @@ #include "content/renderer/media/media_stream_constraints_util.h" #include "content/renderer/media/media_stream_video_track.h" #include "content/renderer/media/video_track_adapter.h" +#include "media/base/bind_to_current_loop.h" namespace content { @@ -350,12 +351,14 @@ bool MediaStreamVideoSource::IsConstraintSupported(const std::string& name) { MediaStreamVideoSource::MediaStreamVideoSource() : state_(NEW), + muted_state_(false), track_adapter_(new VideoTrackAdapter( ChildProcess::current()->io_message_loop_proxy())), weak_factory_(this) { } MediaStreamVideoSource::~MediaStreamVideoSource() { + DCHECK(CalledOnValidThread()); } void MediaStreamVideoSource::AddTrack( @@ -479,6 +482,7 @@ void MediaStreamVideoSource::OnSupportedFormats( bool MediaStreamVideoSource::FindBestFormatWithConstraints( const media::VideoCaptureFormats& formats, media::VideoCaptureFormat* best_format) { + DCHECK(CalledOnValidThread()); // Find the first constraints that we can fulfill. for (std::vector::iterator request_it = requested_constraints_.begin(); @@ -523,6 +527,7 @@ void MediaStreamVideoSource::OnStartDone(bool success) { } void MediaStreamVideoSource::FinalizeAddTrack() { + DCHECK(CalledOnValidThread()); media::VideoCaptureFormats formats; formats.push_back(current_format_); @@ -552,10 +557,16 @@ void MediaStreamVideoSource::FinalizeAddTrack() { double max_frame_rate = 0.0f; GetConstraintValueAsDouble(it->constraints, kMaxFrameRate, &max_frame_rate); + + VideoTrackAdapter::OnMutedCallback on_mute_callback = + media::BindToCurrentLoop(base::Bind( + &MediaStreamVideoSource::SetMutedState, + weak_factory_.GetWeakPtr())); track_adapter_->AddTrack(it->track, it->frame_callback, max_width, max_height, min_aspect_ratio, max_aspect_ratio, - max_frame_rate); + max_frame_rate, current_format_.frame_rate, + on_mute_callback); } DVLOG(3) << "FinalizeAddTrack() success " << success; @@ -568,6 +579,7 @@ void MediaStreamVideoSource::FinalizeAddTrack() { void MediaStreamVideoSource::SetReadyState( blink::WebMediaStreamSource::ReadyState state) { DVLOG(3) << "MediaStreamVideoSource::SetReadyState state " << state; + DCHECK(CalledOnValidThread()); if (!owner().isNull()) { owner().setReadyState(state); } @@ -577,6 +589,16 @@ void MediaStreamVideoSource::SetReadyState( } } +void MediaStreamVideoSource::SetMutedState(bool muted_state) { + DVLOG(3) << "MediaStreamVideoSource::SetMutedState state=" << muted_state; + DCHECK(CalledOnValidThread()); + // WebMediaStreamSource doesn't have a muted state, the tracks do. + for (std::vector::iterator it = tracks_.begin(); + it != tracks_.end(); ++it) { + (*it)->SetMutedState(muted_state); + } +} + MediaStreamVideoSource::RequestedConstraints::RequestedConstraints( MediaStreamVideoTrack* track, const VideoCaptureDeliverFrameCB& frame_callback, diff --git a/content/renderer/media/media_stream_video_source.h b/content/renderer/media/media_stream_video_source.h index 45d2b03082db..c2f6f179366a 100644 --- a/content/renderer/media/media_stream_video_source.h +++ b/content/renderer/media/media_stream_video_source.h @@ -35,9 +35,9 @@ class VideoTrackAdapter; // on a PeerConnection or a source created in NaCl. // All methods calls will be done from the main render thread. // -// When the first track is added to the source by calling AddTrack -// the MediaStreamVideoSource implementation calls GetCurrentSupportedFormats. -// the source implementation must call OnSupportedFormats. +// When the first track is added to the source by calling AddTrack, the +// MediaStreamVideoSource implementation calls GetCurrentSupportedFormats. +// The source implementation must call OnSupportedFormats. // MediaStreamVideoSource then match the constraints provided in AddTrack with // the formats and call StartSourceImpl. The source implementation must call // OnStartDone when the underlying source has been started or failed to start. @@ -88,6 +88,9 @@ class CONTENT_EXPORT MediaStreamVideoSource // Sets ready state and notifies the ready state to all registered tracks. virtual void SetReadyState(blink::WebMediaStreamSource::ReadyState state); + // Sets muted state and notifies it to all registered tracks. + virtual void SetMutedState(bool state); + // An implementation must fetch the formats that can currently be used by // the source and call OnSupportedFormats when done. // |max_requested_height| and |max_requested_width| is the max height and @@ -144,6 +147,7 @@ class CONTENT_EXPORT MediaStreamVideoSource void FinalizeAddTrack(); State state_; + bool muted_state_; media::VideoCaptureFormat current_format_; diff --git a/content/renderer/media/media_stream_video_source_unittest.cc b/content/renderer/media/media_stream_video_source_unittest.cc index 71ee6fca7ecd..efbfbbdd1cae 100644 --- a/content/renderer/media/media_stream_video_source_unittest.cc +++ b/content/renderer/media/media_stream_video_source_unittest.cc @@ -19,6 +19,10 @@ #include "media/base/video_frame.h" #include "testing/gtest/include/gtest/gtest.h" +using ::testing::_; +using ::testing::DoAll; +using ::testing::SaveArg; + namespace content { ACTION_P(RunClosure, closure) { @@ -720,4 +724,87 @@ TEST_F(MediaStreamVideoSourceTest, Use0FpsSupportedFormat) { MediaStreamVideoSink::RemoveFromVideoTrack(&sink, track); } +// Test that a source producing no frames calls back the MSVCS to tell so, and +// this in turn tells the Track attached. Then start passing frames, and check +// that in a reasonable time frame the muted state turns to false. +TEST_F(MediaStreamVideoSourceTest, MutedSource) { + MockMediaConstraintFactory factory; + blink::WebMediaStreamTrack track = + CreateTrackAndStartSource(factory.CreateWebMediaConstraints(), + 640, 480, 30); + + MockMediaStreamVideoSink sink; + MediaStreamVideoSink::AddToVideoTrack(&sink, sink.GetDeliverFrameCB(), track); + EXPECT_EQ(MediaStreamTrack::GetTrack(track)->GetMutedState(), false); + + base::RunLoop run_loop; + base::Closure quit_closure = run_loop.QuitClosure(); + bool muted_state = false; + EXPECT_CALL(*mock_source(), DoSetMutedState(_)) + .WillOnce(DoAll(SaveArg<0>(&muted_state), RunClosure(quit_closure))); + run_loop.Run(); + EXPECT_EQ(muted_state, true); + // TODO(mcasas): When added, check |track|'s (WebMediaStreamTrack) Muted + // attribute, should be true. In the meantime, check the MediaStreamTrack's. + EXPECT_EQ(MediaStreamTrack::GetTrack(track)->GetMutedState(), true); + + EXPECT_CALL(*mock_source(), DoSetMutedState(_)) + .WillOnce(DoAll(SaveArg<0>(&muted_state), RunClosure(quit_closure))); + // Mock frame delivery happens asynchronously, not according to the configured + // frame rate, potentially many frames can pass before the muted state is + // flipped. |kMaxFrameCount| is used as a reasonable high bound of this value. + const int kMaxFrameCount = 10000; + int i = 0; + while (muted_state != false || ++i > kMaxFrameCount) + DeliverVideoFrameAndWaitForRenderer(640, 480, &sink); + EXPECT_EQ(muted_state, false); + EXPECT_LT(i, kMaxFrameCount); + EXPECT_EQ(MediaStreamTrack::GetTrack(track)->GetMutedState(), false); + + MediaStreamVideoSink::RemoveFromVideoTrack(&sink, track); +} + +// Test that a source producing no frames calls back the MSVCS to tell so, and +// this in turn tells all the Tracks attached. +TEST_F(MediaStreamVideoSourceTest, MutedSourceWithTwoTracks) { + MockMediaConstraintFactory factory1; + blink::WebMediaStreamTrack track1 = + CreateTrackAndStartSource(factory1.CreateWebMediaConstraints(), + MediaStreamVideoSource::kDefaultWidth, + MediaStreamVideoSource::kDefaultHeight, + 30); + + MockMediaConstraintFactory factory2; + factory2.AddMandatory(MediaStreamVideoSource::kMaxFrameRate, 15); + blink::WebMediaStreamTrack track2 = CreateTrack( + "123", factory2.CreateWebMediaConstraints()); + EXPECT_EQ(0, NumberOfFailedConstraintsCallbacks()); + + MockMediaStreamVideoSink sink1; + MediaStreamVideoSink::AddToVideoTrack(&sink1, sink1.GetDeliverFrameCB(), + track1); + EXPECT_EQ(MediaStreamTrack::GetTrack(track1)->GetMutedState(), false); + + MockMediaStreamVideoSink sink2; + MediaStreamVideoSink::AddToVideoTrack(&sink2, sink2.GetDeliverFrameCB(), + track2); + EXPECT_EQ(MediaStreamTrack::GetTrack(track2)->GetMutedState(), false); + + base::RunLoop run_loop; + base::Closure quit_closure = run_loop.QuitClosure(); + bool muted_state = false; + EXPECT_CALL(*mock_source(), DoSetMutedState(_)) + .WillOnce(DoAll(SaveArg<0>(&muted_state), RunClosure(quit_closure))); + run_loop.Run(); + EXPECT_EQ(muted_state, true); + + // TODO(mcasas): When added, check |track|'s (WebMediaStreamTrack) Muted + // attribute, should be true. In the meantime, check the MediaStreamTrack's. + EXPECT_EQ(MediaStreamTrack::GetTrack(track1)->GetMutedState(), true); + EXPECT_EQ(MediaStreamTrack::GetTrack(track2)->GetMutedState(), true); + + MediaStreamVideoSink::RemoveFromVideoTrack(&sink1, track1); + MediaStreamVideoSink::RemoveFromVideoTrack(&sink2, track2); +} + } // namespace content diff --git a/content/renderer/media/media_stream_video_track.cc b/content/renderer/media/media_stream_video_track.cc index 67da1ad79c82..213d9b6385bd 100644 --- a/content/renderer/media/media_stream_video_track.cc +++ b/content/renderer/media/media_stream_video_track.cc @@ -7,7 +7,6 @@ #include #include "base/bind.h" -#include "media/base/bind_to_current_loop.h" namespace content { @@ -240,4 +239,14 @@ void MediaStreamVideoTrack::OnReadyStateChanged( } } +void MediaStreamVideoTrack::SetMutedState(bool muted_state) { + DCHECK(thread_checker_.CalledOnValidThread()); + muted_state_ = muted_state; +} + +bool MediaStreamVideoTrack::GetMutedState(void) const { + DCHECK(thread_checker_.CalledOnValidThread()); + return muted_state_; +} + } // namespace content diff --git a/content/renderer/media/media_stream_video_track.h b/content/renderer/media/media_stream_video_track.h index 6fecbe1758ed..a99d1a4e3c30 100644 --- a/content/renderer/media/media_stream_video_track.h +++ b/content/renderer/media/media_stream_video_track.h @@ -50,6 +50,9 @@ class CONTENT_EXPORT MediaStreamVideoTrack : public MediaStreamTrack { virtual ~MediaStreamVideoTrack(); virtual void SetEnabled(bool enabled) OVERRIDE; + virtual void SetMutedState(bool state) OVERRIDE; + virtual bool GetMutedState(void) const OVERRIDE; + virtual void Stop() OVERRIDE; void OnReadyStateChanged(blink::WebMediaStreamSource::ReadyState state); diff --git a/content/renderer/media/mock_media_stream_video_source.h b/content/renderer/media/mock_media_stream_video_source.h index ad7463b6d4c7..0786798789c0 100644 --- a/content/renderer/media/mock_media_stream_video_source.h +++ b/content/renderer/media/mock_media_stream_video_source.h @@ -7,6 +7,8 @@ #include "content/renderer/media/media_stream_video_source.h" +#include "testing/gmock/include/gmock/gmock.h" + namespace content { class MockMediaStreamVideoSource : public MediaStreamVideoSource { @@ -14,6 +16,8 @@ class MockMediaStreamVideoSource : public MediaStreamVideoSource { explicit MockMediaStreamVideoSource(bool manual_get_supported_formats); virtual ~MockMediaStreamVideoSource(); + MOCK_METHOD1(DoSetMutedState, void(bool muted_state)); + // Simulate that the underlying source start successfully. void StartMockedSource(); @@ -39,6 +43,11 @@ class MockMediaStreamVideoSource : public MediaStreamVideoSource { int max_requested_height() const { return max_requested_height_; } int max_requested_width() const { return max_requested_width_; } + void SetMutedState(bool muted_state) { + MediaStreamVideoSource::SetMutedState(muted_state); + DoSetMutedState(muted_state); + } + protected: void DeliverVideoFrameOnIO(const scoped_refptr& frame, media::VideoCaptureFormat format, diff --git a/content/renderer/media/video_track_adapter.cc b/content/renderer/media/video_track_adapter.cc index e51f8427bef2..13b79bd5e4ca 100644 --- a/content/renderer/media/video_track_adapter.cc +++ b/content/renderer/media/video_track_adapter.cc @@ -17,6 +17,12 @@ namespace content { namespace { +// Amount of frame intervals to wait before considering the source as muted, for +// the first frame and under normal conditions, respectively. First frame might +// take longer to arrive due to source startup. +const float kFirstFrameTimeoutInFrameIntervals = 100.0f; +const float kNormalFrameTimeoutInFrameIntervals = 25.0f; + // Empty method used for keeping a reference to the original media::VideoFrame // in VideoFrameResolutionAdapter::DeliverFrame if cropping is needed. // The reference to |frame| is kept in the closure that calls this method. @@ -301,7 +307,9 @@ bool VideoTrackAdapter::VideoFrameResolutionAdapter::IsEmpty() const { VideoTrackAdapter::VideoTrackAdapter( const scoped_refptr& io_message_loop) : io_message_loop_(io_message_loop), - renderer_task_runner_(base::MessageLoopProxy::current()) { + renderer_task_runner_(base::MessageLoopProxy::current()), + frame_counter_(0), + source_frame_rate_(0.0f) { DCHECK(io_message_loop_); } @@ -309,14 +317,23 @@ VideoTrackAdapter::~VideoTrackAdapter() { DCHECK(adapters_.empty()); } -void VideoTrackAdapter::AddTrack(const MediaStreamVideoTrack* track, - VideoCaptureDeliverFrameCB frame_callback, - int max_width, - int max_height, - double min_aspect_ratio, - double max_aspect_ratio, - double max_frame_rate) { +void VideoTrackAdapter::AddTrack( + const MediaStreamVideoTrack* track, + VideoCaptureDeliverFrameCB frame_callback, + int max_width, + int max_height, + double min_aspect_ratio, + double max_aspect_ratio, + double max_frame_rate, + double source_frame_rate, + const OnMutedCallback& on_muted_state_callback) { DCHECK(thread_checker_.CalledOnValidThread()); + // Track monitoring should be scheduled before AddTrackOnIO() so it can find + // |adapters_| empty. + io_message_loop_->PostTask( + FROM_HERE, + base::Bind(&VideoTrackAdapter::StartTrackMonitoringOnIO, + this, on_muted_state_callback, source_frame_rate)); io_message_loop_->PostTask( FROM_HERE, base::Bind(&VideoTrackAdapter::AddTrackOnIO, @@ -360,6 +377,26 @@ void VideoTrackAdapter::RemoveTrack(const MediaStreamVideoTrack* track) { base::Bind(&VideoTrackAdapter::RemoveTrackOnIO, this, track)); } +void VideoTrackAdapter::StartTrackMonitoringOnIO( + const OnMutedCallback& on_muted_state_callback, + double source_frame_rate) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + // Only trigger monitoring for the first Track. + if (!adapters_.empty()) + return; + // If the source does not know the frame rate, set one by default. + if (source_frame_rate == 0.0f) + source_frame_rate = MediaStreamVideoSource::kDefaultFrameRate; + source_frame_rate_ = source_frame_rate; + DVLOG(1) << "Monitoring frame creation, first (large) delay: " + << (kFirstFrameTimeoutInFrameIntervals / source_frame_rate_) << "s"; + io_message_loop_->PostDelayedTask(FROM_HERE, + base::Bind(&VideoTrackAdapter::CheckFramesReceivedOnIO, this, + on_muted_state_callback, frame_counter_), + base::TimeDelta::FromSecondsD(kFirstFrameTimeoutInFrameIntervals / + source_frame_rate_)); +} + void VideoTrackAdapter::RemoveTrackOnIO(const MediaStreamVideoTrack* track) { DCHECK(io_message_loop_->BelongsToCurrentThread()); for (FrameAdapters::iterator it = adapters_.begin(); @@ -378,10 +415,28 @@ void VideoTrackAdapter::DeliverFrameOnIO( const base::TimeTicks& estimated_capture_time) { DCHECK(io_message_loop_->BelongsToCurrentThread()); TRACE_EVENT0("video", "VideoTrackAdapter::DeliverFrameOnIO"); + ++frame_counter_; for (FrameAdapters::iterator it = adapters_.begin(); it != adapters_.end(); ++it) { (*it)->DeliverFrame(frame, format, estimated_capture_time); } } +void VideoTrackAdapter::CheckFramesReceivedOnIO( + const OnMutedCallback& set_muted_state_callback, + uint64 old_frame_counter_snapshot) { + DCHECK(io_message_loop_->BelongsToCurrentThread()); + DVLOG_IF(1, old_frame_counter_snapshot == frame_counter_) + << "No frames have passed, setting source as Muted."; + set_muted_state_callback.Run(old_frame_counter_snapshot == frame_counter_); + + // Rearm the monitoring while there are active Tracks, i.e. as long as the + // owner MediaStreamSource is active. + io_message_loop_->PostDelayedTask(FROM_HERE, + base::Bind(&VideoTrackAdapter::CheckFramesReceivedOnIO, this, + set_muted_state_callback, frame_counter_), + base::TimeDelta::FromSecondsD(kNormalFrameTimeoutInFrameIntervals / + source_frame_rate_)); +} + } // namespace content diff --git a/content/renderer/media/video_track_adapter.h b/content/renderer/media/video_track_adapter.h index 0df314d27993..c26ca142981f 100644 --- a/content/renderer/media/video_track_adapter.h +++ b/content/renderer/media/video_track_adapter.h @@ -27,6 +27,8 @@ namespace content { class VideoTrackAdapter : public base::RefCountedThreadSafe { public: + typedef base::Callback OnMutedCallback; + explicit VideoTrackAdapter( const scoped_refptr& io_message_loop); @@ -34,12 +36,16 @@ class VideoTrackAdapter // a resolution within the boundaries of the arguments. // Must be called on the main render thread. |frame_callback| is guaranteed to // be released on the main render thread. + // |source_frame_rate| is used to calculate a prudent interval to check for + // passing frames and inform of the result via |on_muted_state_callback|. void AddTrack(const MediaStreamVideoTrack* track, VideoCaptureDeliverFrameCB frame_callback, int max_width, int max_height, double min_aspect_ratio, double max_aspect_ratio, - double max_frame_rate); + double max_frame_rate, + double source_frame_rate, + const OnMutedCallback& on_muted_state_callback); void RemoveTrack(const MediaStreamVideoTrack* track); // Delivers |frame| to all tracks that have registered a callback. @@ -67,6 +73,15 @@ class VideoTrackAdapter double max_frame_rate); void RemoveTrackOnIO(const MediaStreamVideoTrack* track); + void StartTrackMonitoringOnIO( + const OnMutedCallback& on_muted_state_callback, + double source_frame_rate); + + // Compare |frame_counter_snapshot| with the current |frame_counter_|, and + // inform of the situation (muted, not muted) via |set_muted_state_callback|. + void CheckFramesReceivedOnIO(const OnMutedCallback& set_muted_state_callback, + uint64 old_frame_counter_snapshot); + // |thread_checker_| is bound to the main render thread. base::ThreadChecker thread_checker_; @@ -84,6 +99,12 @@ class VideoTrackAdapter FrameAdapters; FrameAdapters adapters_; + // Running frame counter, accessed on the IO-thread. + uint64 frame_counter_; + + // Frame rate configured on the video source, accessed on the IO-thread. + float source_frame_rate_; + DISALLOW_COPY_AND_ASSIGN(VideoTrackAdapter); }; -- 2.11.4.GIT