Add support for content hints to VideoTrack.
Permits overriding the source-default is_screencast option to be able to
treat screencast sources as fluid video, preserving motion at the loss
of individual frame quality (or vice versa).
BUG=chromium:653531
R=deadbeef@webrtc.org
Review-Url: https://codereview.webrtc.org/2579993003
Cr-Commit-Position: refs/heads/master@{#15659}
diff --git a/webrtc/api/mediastreaminterface.h b/webrtc/api/mediastreaminterface.h
index baebad7..eb51633 100644
--- a/webrtc/api/mediastreaminterface.h
+++ b/webrtc/api/mediastreaminterface.h
@@ -133,13 +133,21 @@
: public MediaStreamTrackInterface,
public rtc::VideoSourceInterface<VideoFrame> {
public:
+ // Video track content hint, used to override the source is_screencast
+ // property.
+ // See https://crbug.com/653531 and https://github.com/WICG/mst-content-hint.
+ enum class ContentHint { kNone, kFluid, kDetailed };
+
// Register a video sink for this track.
void AddOrUpdateSink(rtc::VideoSinkInterface<VideoFrame>* sink,
- const rtc::VideoSinkWants& wants) override{};
- void RemoveSink(rtc::VideoSinkInterface<VideoFrame>* sink) override{};
+ const rtc::VideoSinkWants& wants) override {}
+ void RemoveSink(rtc::VideoSinkInterface<VideoFrame>* sink) override {}
virtual VideoTrackSourceInterface* GetSource() const = 0;
+ virtual ContentHint content_hint() const { return ContentHint::kNone; }
+ virtual void set_content_hint(ContentHint hint) {}
+
protected:
virtual ~VideoTrackInterface() {}
};
diff --git a/webrtc/api/mediastreamtrackproxy.h b/webrtc/api/mediastreamtrackproxy.h
index 206a467..fc73536 100644
--- a/webrtc/api/mediastreamtrackproxy.h
+++ b/webrtc/api/mediastreamtrackproxy.h
@@ -41,6 +41,8 @@
PROXY_CONSTMETHOD0(TrackState, state)
PROXY_CONSTMETHOD0(bool, enabled)
PROXY_METHOD1(bool, set_enabled, bool)
+ PROXY_CONSTMETHOD0(ContentHint, content_hint)
+ PROXY_METHOD1(void, set_content_hint, ContentHint)
PROXY_WORKER_METHOD2(void,
AddOrUpdateSink,
rtc::VideoSinkInterface<VideoFrame>*,
diff --git a/webrtc/api/rtpsender.cc b/webrtc/api/rtpsender.cc
index 2e01d96..61f53b4 100644
--- a/webrtc/api/rtpsender.cc
+++ b/webrtc/api/rtpsender.cc
@@ -243,7 +243,8 @@
stream_id_(stream_id),
channel_(channel),
track_(track),
- cached_track_enabled_(track->enabled()) {
+ cached_track_enabled_(track->enabled()),
+ cached_track_content_hint_(track->content_hint()) {
track_->RegisterObserver(this);
}
@@ -253,7 +254,8 @@
stream_id_(rtc::CreateRandomUuid()),
channel_(channel),
track_(track),
- cached_track_enabled_(track->enabled()) {
+ cached_track_enabled_(track->enabled()),
+ cached_track_content_hint_(track->content_hint()) {
track_->RegisterObserver(this);
}
@@ -269,8 +271,10 @@
void VideoRtpSender::OnChanged() {
TRACE_EVENT0("webrtc", "VideoRtpSender::OnChanged");
RTC_DCHECK(!stopped_);
- if (cached_track_enabled_ != track_->enabled()) {
+ if (cached_track_enabled_ != track_->enabled() ||
+ cached_track_content_hint_ != track_->content_hint()) {
cached_track_enabled_ = track_->enabled();
+ cached_track_content_hint_ = track_->content_hint();
if (can_send_track()) {
SetVideoSend();
}
@@ -303,6 +307,7 @@
track_ = video_track;
if (track_) {
cached_track_enabled_ = track_->enabled();
+ cached_track_content_hint_ = track_->content_hint();
track_->RegisterObserver(this);
}
@@ -372,6 +377,16 @@
options.is_screencast = rtc::Optional<bool>(source->is_screencast());
options.video_noise_reduction = source->needs_denoising();
}
+ switch (cached_track_content_hint_) {
+ case VideoTrackInterface::ContentHint::kNone:
+ break;
+ case VideoTrackInterface::ContentHint::kFluid:
+ options.is_screencast = rtc::Optional<bool>(false);
+ break;
+ case VideoTrackInterface::ContentHint::kDetailed:
+ options.is_screencast = rtc::Optional<bool>(true);
+ break;
+ }
if (!channel_->SetVideoSend(ssrc_, track_->enabled(), &options, track_)) {
RTC_DCHECK(false);
}
diff --git a/webrtc/api/rtpsender.h b/webrtc/api/rtpsender.h
index 067ae5e..6c05921 100644
--- a/webrtc/api/rtpsender.h
+++ b/webrtc/api/rtpsender.h
@@ -225,6 +225,8 @@
rtc::scoped_refptr<VideoTrackInterface> track_;
uint32_t ssrc_ = 0;
bool cached_track_enabled_ = false;
+ VideoTrackInterface::ContentHint cached_track_content_hint_ =
+ VideoTrackInterface::ContentHint::kNone;
bool stopped_ = false;
};
diff --git a/webrtc/api/rtpsenderreceiver_unittest.cc b/webrtc/api/rtpsenderreceiver_unittest.cc
index f4f550e..a79b297 100644
--- a/webrtc/api/rtpsenderreceiver_unittest.cc
+++ b/webrtc/api/rtpsenderreceiver_unittest.cc
@@ -101,9 +101,11 @@
void TearDown() override { channel_manager_.Terminate(); }
- void AddVideoTrack() {
+ void AddVideoTrack() { AddVideoTrack(false); }
+
+ void AddVideoTrack(bool is_screencast) {
rtc::scoped_refptr<VideoTrackSourceInterface> source(
- FakeVideoTrackSource::Create());
+ FakeVideoTrackSource::Create(is_screencast));
video_track_ = VideoTrack::Create(kVideoTrackId, source);
EXPECT_TRUE(stream_->AddTrack(video_track_));
}
@@ -120,8 +122,10 @@
VerifyVoiceChannelInput();
}
- void CreateVideoRtpSender() {
- AddVideoTrack();
+ void CreateVideoRtpSender() { CreateVideoRtpSender(false); }
+
+ void CreateVideoRtpSender(bool is_screencast) {
+ AddVideoTrack(is_screencast);
video_rtp_sender_ = new VideoRtpSender(stream_->GetVideoTracks()[0],
stream_->label(), video_channel_);
video_rtp_sender_->SetSsrc(kVideoSsrc);
@@ -621,4 +625,95 @@
DestroyVideoRtpReceiver();
}
+// Test that makes sure that a video track content hint translates to the proper
+// value for sources that are not screencast.
+TEST_F(RtpSenderReceiverTest, PropagatesVideoTrackContentHint) {
+ CreateVideoRtpSender();
+
+ video_track_->set_enabled(true);
+
+ // |video_track_| is not screencast by default.
+ EXPECT_EQ(rtc::Optional<bool>(false),
+ video_media_channel_->options().is_screencast);
+ // No content hint should be set by default.
+ EXPECT_EQ(VideoTrackInterface::ContentHint::kNone,
+ video_track_->content_hint());
+ // Setting detailed should turn a non-screencast source into screencast mode.
+ video_track_->set_content_hint(VideoTrackInterface::ContentHint::kDetailed);
+ EXPECT_EQ(rtc::Optional<bool>(true),
+ video_media_channel_->options().is_screencast);
+ // Removing the content hint should turn the track back into non-screencast
+ // mode.
+ video_track_->set_content_hint(VideoTrackInterface::ContentHint::kNone);
+ EXPECT_EQ(rtc::Optional<bool>(false),
+ video_media_channel_->options().is_screencast);
+ // Setting fluid should remain in non-screencast mode (its default).
+ video_track_->set_content_hint(VideoTrackInterface::ContentHint::kFluid);
+ EXPECT_EQ(rtc::Optional<bool>(false),
+ video_media_channel_->options().is_screencast);
+
+ DestroyVideoRtpSender();
+}
+
+// Test that makes sure that a video track content hint translates to the proper
+// value for screencast sources.
+TEST_F(RtpSenderReceiverTest,
+ PropagatesVideoTrackContentHintForScreencastSource) {
+ CreateVideoRtpSender(true);
+
+ video_track_->set_enabled(true);
+
+ // |video_track_| with a screencast source should be screencast by default.
+ EXPECT_EQ(rtc::Optional<bool>(true),
+ video_media_channel_->options().is_screencast);
+ // No content hint should be set by default.
+ EXPECT_EQ(VideoTrackInterface::ContentHint::kNone,
+ video_track_->content_hint());
+ // Setting fluid should turn a screencast source into non-screencast mode.
+ video_track_->set_content_hint(VideoTrackInterface::ContentHint::kFluid);
+ EXPECT_EQ(rtc::Optional<bool>(false),
+ video_media_channel_->options().is_screencast);
+ // Removing the content hint should turn the track back into screencast mode.
+ video_track_->set_content_hint(VideoTrackInterface::ContentHint::kNone);
+ EXPECT_EQ(rtc::Optional<bool>(true),
+ video_media_channel_->options().is_screencast);
+ // Setting detailed should still remain in screencast mode (its default).
+ video_track_->set_content_hint(VideoTrackInterface::ContentHint::kDetailed);
+ EXPECT_EQ(rtc::Optional<bool>(true),
+ video_media_channel_->options().is_screencast);
+
+ DestroyVideoRtpSender();
+}
+
+// Test that makes sure any content hints that are set on a track before
+// VideoRtpSender is ready to send are still applied when it gets ready to send.
+TEST_F(RtpSenderReceiverTest,
+ PropagatesVideoTrackContentHintSetBeforeEnabling) {
+ AddVideoTrack();
+ // Setting detailed overrides the default non-screencast mode. This should be
+ // applied even if the track is set on construction.
+ video_track_->set_content_hint(VideoTrackInterface::ContentHint::kDetailed);
+ video_rtp_sender_ = new VideoRtpSender(stream_->GetVideoTracks()[0],
+ stream_->label(), video_channel_);
+ video_track_->set_enabled(true);
+
+ // Sender is not ready to send (no SSRC) so no option should have been set.
+ EXPECT_EQ(rtc::Optional<bool>(),
+ video_media_channel_->options().is_screencast);
+
+ // Verify that the content hint is accounted for when video_rtp_sender_ does
+ // get enabled.
+ video_rtp_sender_->SetSsrc(kVideoSsrc);
+ EXPECT_EQ(rtc::Optional<bool>(true),
+ video_media_channel_->options().is_screencast);
+
+ // And removing the hint should go back to false (to verify that false was
+ // default correctly).
+ video_track_->set_content_hint(VideoTrackInterface::ContentHint::kNone);
+ EXPECT_EQ(rtc::Optional<bool>(false),
+ video_media_channel_->options().is_screencast);
+
+ DestroyVideoRtpSender();
+}
+
} // namespace webrtc
diff --git a/webrtc/api/test/fakevideotracksource.h b/webrtc/api/test/fakevideotracksource.h
index 1cb264b..0638a1f 100644
--- a/webrtc/api/test/fakevideotracksource.h
+++ b/webrtc/api/test/fakevideotracksource.h
@@ -19,22 +19,29 @@
class FakeVideoTrackSource : public VideoTrackSource {
public:
+ static rtc::scoped_refptr<FakeVideoTrackSource> Create(bool is_screencast) {
+ return new rtc::RefCountedObject<FakeVideoTrackSource>(is_screencast);
+ }
+
static rtc::scoped_refptr<FakeVideoTrackSource> Create() {
- return new rtc::RefCountedObject<FakeVideoTrackSource>();
+ return Create(false);
}
cricket::FakeVideoCapturer* fake_video_capturer() {
return &fake_video_capturer_;
}
+ bool is_screencast() const override { return is_screencast_; }
+
protected:
- FakeVideoTrackSource()
- : VideoTrackSource(&fake_video_capturer_,
- false /* remote */) {}
+ explicit FakeVideoTrackSource(bool is_screencast)
+ : VideoTrackSource(&fake_video_capturer_, false /* remote */),
+ is_screencast_(is_screencast) {}
virtual ~FakeVideoTrackSource() {}
private:
cricket::FakeVideoCapturer fake_video_capturer_;
+ const bool is_screencast_;
};
} // namespace webrtc
diff --git a/webrtc/api/videotrack.cc b/webrtc/api/videotrack.cc
index b47a320..2fa2ba6 100644
--- a/webrtc/api/videotrack.cc
+++ b/webrtc/api/videotrack.cc
@@ -19,7 +19,8 @@
VideoTrack::VideoTrack(const std::string& label,
VideoTrackSourceInterface* video_source)
: MediaStreamTrack<VideoTrackInterface>(label),
- video_source_(video_source) {
+ video_source_(video_source),
+ content_hint_(ContentHint::kNone) {
worker_thread_checker_.DetachFromThread();
video_source_->RegisterObserver(this);
}
@@ -49,6 +50,19 @@
video_source_->RemoveSink(sink);
}
+VideoTrackInterface::ContentHint VideoTrack::content_hint() const {
+ RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
+ return content_hint_;
+}
+
+void VideoTrack::set_content_hint(ContentHint hint) {
+ RTC_DCHECK_RUN_ON(&signaling_thread_checker_);
+ if (content_hint_ == hint)
+ return;
+ content_hint_ = hint;
+ Notifier<VideoTrackInterface>::FireOnChanged();
+}
+
bool VideoTrack::set_enabled(bool enable) {
RTC_DCHECK(signaling_thread_checker_.CalledOnValidThread());
for (auto& sink_pair : sink_pairs()) {
diff --git a/webrtc/api/videotrack.h b/webrtc/api/videotrack.h
index cbebec3..ebea848 100644
--- a/webrtc/api/videotrack.h
+++ b/webrtc/api/videotrack.h
@@ -36,6 +36,8 @@
VideoTrackSourceInterface* GetSource() const override {
return video_source_.get();
}
+ ContentHint content_hint() const override;
+ void set_content_hint(ContentHint hint) override;
bool set_enabled(bool enable) override;
std::string kind() const override;
@@ -50,6 +52,7 @@
rtc::ThreadChecker signaling_thread_checker_;
rtc::ThreadChecker worker_thread_checker_;
rtc::scoped_refptr<VideoTrackSourceInterface> video_source_;
+ ContentHint content_hint_ GUARDED_BY(signaling_thread_checker_);
};
} // namespace webrtc