diff --git a/api/rtp_parameters.h b/api/rtp_parameters.h index 91455e2912..09473a6ce9 100644 --- a/api/rtp_parameters.h +++ b/api/rtp_parameters.h @@ -515,6 +515,7 @@ struct RTC_EXPORT RtpEncodingParameters { // Value to use for RID RTP header extension. // Called "encodingId" in ORTC. std::string rid; + bool request_key_frame = false; // Allow dynamic frame length changes for audio: // https://w3c.github.io/webrtc-extensions/#dom-rtcrtpencodingparameters-adaptiveptime diff --git a/media/engine/fake_webrtc_call.cc b/media/engine/fake_webrtc_call.cc index 99a8d7bce7..846be4d7ae 100644 --- a/media/engine/fake_webrtc_call.cc +++ b/media/engine/fake_webrtc_call.cc @@ -383,6 +383,11 @@ void FakeVideoSendStream::SetSource( : rtc::VideoSinkWants()); } +void FakeVideoSendStream::GenerateKeyFrame( + const std::vector& rids) { + keyframes_requested_by_rid_ = rids; +} + void FakeVideoSendStream::InjectVideoSinkWants( const rtc::VideoSinkWants& wants) { sink_wants_ = wants; diff --git a/media/engine/fake_webrtc_call.h b/media/engine/fake_webrtc_call.h index 92ff1b84d7..8d9ecb6396 100644 --- a/media/engine/fake_webrtc_call.h +++ b/media/engine/fake_webrtc_call.h @@ -188,7 +188,10 @@ class FakeVideoSendStream final rtc::VideoSourceInterface* source() const { return source_; } - void GenerateKeyFrame(const std::vector& rids) override {} + void GenerateKeyFrame(const std::vector& rids); + const std::vector& GetKeyFramesRequested() const { + return keyframes_requested_by_rid_; + } private: // rtc::VideoSinkInterface implementation. @@ -231,6 +234,7 @@ class FakeVideoSendStream final absl::optional last_frame_; webrtc::VideoSendStream::Stats stats_; int num_encoder_reconfigurations_ = 0; + std::vector keyframes_requested_by_rid_; }; class FakeVideoReceiveStream final diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index 504aad9002..056478c384 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -1965,6 +1965,22 @@ WebRtcVideoSendChannel::WebRtcVideoSendStream::SetRtpParameters( stream_->SetSource(source_, GetDegradationPreference()); } } + // Check if a key frame was requested via setParameters. + std::vector key_frames_requested_by_rid; + for (const auto& encoding : rtp_parameters_.encodings) { + if (encoding.request_key_frame) { + key_frames_requested_by_rid.push_back(encoding.rid); + } + } + if (!key_frames_requested_by_rid.empty()) { + if (key_frames_requested_by_rid.size() == 1 && + key_frames_requested_by_rid[0] == "") { + // For non-simulcast cases there is no rid, + // request a keyframe on all layers. + key_frames_requested_by_rid.clear(); + } + GenerateKeyFrame(key_frames_requested_by_rid); + } return webrtc::InvokeSetParametersCallback(callback, webrtc::RTCError::OK()); } diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc index 887588341f..d228ab4c9c 100644 --- a/media/engine/webrtc_video_engine_unittest.cc +++ b/media/engine/webrtc_video_engine_unittest.cc @@ -9540,6 +9540,84 @@ TEST_F(WebRtcVideoChannelTest, EXPECT_TRUE(stream->GetEncoderConfig().is_quality_scaling_allowed); } +TEST_F(WebRtcVideoChannelTest, GenerateKeyFrameSinglecast) { + FakeVideoSendStream* stream = AddSendStream(); + + webrtc::RtpParameters rtp_parameters = + send_channel_->GetRtpSendParameters(last_ssrc_); + ASSERT_EQ(1u, rtp_parameters.encodings.size()); + EXPECT_EQ(rtp_parameters.encodings[0].rid, ""); + EXPECT_TRUE( + send_channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters).ok()); + EXPECT_THAT(stream->GetKeyFramesRequested(), std::vector({})); + + // Manually set the key frames requested to check they are cleared by the next + // call. + stream->GenerateKeyFrame({"bogus"}); + rtp_parameters.encodings[0].request_key_frame = true; + EXPECT_TRUE( + send_channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters).ok()); + EXPECT_THAT(stream->GetKeyFramesRequested(), + ElementsAreArray(std::vector({}))); +} + +TEST_F(WebRtcVideoChannelTest, GenerateKeyFrameSimulcast) { + StreamParams stream_params = CreateSimStreamParams("cname", {123, 456, 789}); + + std::vector rids = {"f", "h", "q"}; + std::vector rid_descriptions; + for (const auto& rid : rids) { + rid_descriptions.emplace_back(rid, cricket::RidDirection::kSend); + } + stream_params.set_rids(rid_descriptions); + FakeVideoSendStream* stream = AddSendStream(stream_params); + + webrtc::RtpParameters rtp_parameters = + send_channel_->GetRtpSendParameters(last_ssrc_); + ASSERT_EQ(3u, rtp_parameters.encodings.size()); + EXPECT_EQ(rtp_parameters.encodings[0].rid, "f"); + EXPECT_EQ(rtp_parameters.encodings[1].rid, "h"); + EXPECT_EQ(rtp_parameters.encodings[2].rid, "q"); + + EXPECT_TRUE( + send_channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters).ok()); + EXPECT_THAT(stream->GetKeyFramesRequested(), + ElementsAreArray(std::vector({}))); + + rtp_parameters.encodings[0].request_key_frame = true; + EXPECT_TRUE( + send_channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters).ok()); + EXPECT_THAT(stream->GetKeyFramesRequested(), ElementsAreArray({"f"})); + + rtp_parameters.encodings[0].request_key_frame = true; + rtp_parameters.encodings[1].request_key_frame = true; + EXPECT_TRUE( + send_channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters).ok()); + EXPECT_THAT(stream->GetKeyFramesRequested(), ElementsAreArray({"f", "h"})); + + rtp_parameters.encodings[0].request_key_frame = true; + rtp_parameters.encodings[1].request_key_frame = true; + rtp_parameters.encodings[2].request_key_frame = true; + EXPECT_TRUE( + send_channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters).ok()); + EXPECT_THAT(stream->GetKeyFramesRequested(), + ElementsAreArray({"f", "h", "q"})); + + rtp_parameters.encodings[0].request_key_frame = true; + rtp_parameters.encodings[1].request_key_frame = false; + rtp_parameters.encodings[2].request_key_frame = true; + EXPECT_TRUE( + send_channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters).ok()); + EXPECT_THAT(stream->GetKeyFramesRequested(), ElementsAreArray({"f", "q"})); + + rtp_parameters.encodings[0].request_key_frame = false; + rtp_parameters.encodings[1].request_key_frame = false; + rtp_parameters.encodings[2].request_key_frame = true; + EXPECT_TRUE( + send_channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters).ok()); + EXPECT_THAT(stream->GetKeyFramesRequested(), ElementsAreArray({"q"})); +} + class WebRtcVideoChannelSimulcastTest : public ::testing::Test { public: WebRtcVideoChannelSimulcastTest()