From bb894ffcb4d0fc917bd60120e6ea49993f674673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20M=C3=B6ller?= Date: Thu, 15 Mar 2018 12:28:53 +0100 Subject: [PATCH] Make PayloadRouter own the picture id and tl0 pic idx sequences. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It previously owned only the picture id and only in the WebRTC-VP8-Forced-Fallback-Encoder-v2 experiment. Moving responsibility to PayloadRouter ensures that both picture id and tl0 idx are continuous over codec changes, as required by the specs for VP8 and VP9 over RTP. Bug: webrtc:8830 Change-Id: Ie77356dfec6d1e372b6970189e4c3888451920e6 Reviewed-on: https://webrtc-review.googlesource.com/61640 Commit-Queue: Niels Moller Reviewed-by: Åsa Persson Reviewed-by: Rasmus Brandt Cr-Commit-Position: refs/heads/master@{#22448} --- modules/video_coding/codecs/vp9/vp9_impl.cc | 2 + .../include/video_codec_interface.h | 5 + video/payload_router.cc | 66 ++++-- video/payload_router.h | 2 +- video/payload_router_unittest.cc | 218 +++++++++++------- 5 files changed, 187 insertions(+), 106 deletions(-) diff --git a/modules/video_coding/codecs/vp9/vp9_impl.cc b/modules/video_coding/codecs/vp9/vp9_impl.cc index c7c6247837..06f485ba8c 100644 --- a/modules/video_coding/codecs/vp9/vp9_impl.cc +++ b/modules/video_coding/codecs/vp9/vp9_impl.cc @@ -646,6 +646,8 @@ void VP9EncoderImpl::PopulateCodecSpecific(CodecSpecificInfo* codec_specific, vp9_info->inter_layer_predicted = true; } + vp9_info->first_frame_in_picture = is_first_frame; + if (pkt.data.frame.flags & VPX_FRAME_IS_KEY) { frames_since_kf_ = 0; } diff --git a/modules/video_coding/include/video_codec_interface.h b/modules/video_coding/include/video_codec_interface.h index 6616053c25..7524631f99 100644 --- a/modules/video_coding/include/video_codec_interface.h +++ b/modules/video_coding/include/video_codec_interface.h @@ -28,23 +28,28 @@ class RTPFragmentationHeader; // forward declaration // Note: if any pointers are added to this struct, it must be fitted // with a copy-constructor. See below. struct CodecSpecificInfoVP8 { + // TODO(nisse): Delete, set by PayloadRouter. int16_t pictureId; // Negative value to skip pictureId. bool nonReference; uint8_t simulcastIdx; uint8_t temporalIdx; bool layerSync; + // TODO(nisse): Delete, set by PayloadRouter. int tl0PicIdx; // Negative value to skip tl0PicIdx. int8_t keyIdx; // Negative value to skip keyIdx. }; struct CodecSpecificInfoVP9 { + // TODO(nisse): Delete, set by PayloadRouter. int16_t picture_id; // Negative value to skip pictureId. + bool first_frame_in_picture; // First frame, increment picture_id. bool inter_pic_predicted; // This layer frame is dependent on previously // coded frame(s). bool flexible_mode; bool ss_data_available; + // TODO(nisse): Delete, set by PayloadRouter. int tl0_pic_idx; // Negative value to skip tl0PicIdx. uint8_t temporal_idx; uint8_t spatial_idx; diff --git a/video/payload_router.cc b/video/payload_router.cc index 4ecb620c7c..f980bc41bf 100644 --- a/video/payload_router.cc +++ b/video/payload_router.cc @@ -16,7 +16,6 @@ #include "rtc_base/checks.h" #include "rtc_base/random.h" #include "rtc_base/timeutils.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { @@ -28,11 +27,9 @@ void CopyCodecSpecific(const CodecSpecificInfo* info, RTPVideoHeader* rtp) { case kVideoCodecVP8: { rtp->codec = kRtpVideoVp8; rtp->codecHeader.VP8.InitRTPVideoHeaderVP8(); - rtp->codecHeader.VP8.pictureId = info->codecSpecific.VP8.pictureId; rtp->codecHeader.VP8.nonReference = info->codecSpecific.VP8.nonReference; rtp->codecHeader.VP8.temporalIdx = info->codecSpecific.VP8.temporalIdx; rtp->codecHeader.VP8.layerSync = info->codecSpecific.VP8.layerSync; - rtp->codecHeader.VP8.tl0PicIdx = info->codecSpecific.VP8.tl0PicIdx; rtp->codecHeader.VP8.keyIdx = info->codecSpecific.VP8.keyIdx; rtp->simulcastIdx = info->codecSpecific.VP8.simulcastIdx; return; @@ -46,8 +43,6 @@ void CopyCodecSpecific(const CodecSpecificInfo* info, RTPVideoHeader* rtp) { info->codecSpecific.VP9.flexible_mode; rtp->codecHeader.VP9.ss_data_available = info->codecSpecific.VP9.ss_data_available; - rtp->codecHeader.VP9.picture_id = info->codecSpecific.VP9.picture_id; - rtp->codecHeader.VP9.tl0_pic_idx = info->codecSpecific.VP9.tl0_pic_idx; rtp->codecHeader.VP9.temporal_idx = info->codecSpecific.VP9.temporal_idx; rtp->codecHeader.VP9.spatial_idx = info->codecSpecific.VP9.spatial_idx; rtp->codecHeader.VP9.temporal_up_switch = @@ -93,9 +88,8 @@ void CopyCodecSpecific(const CodecSpecificInfo* info, RTPVideoHeader* rtp) { } // namespace -// Currently only used if forced fallback for VP8 is enabled. -// Consider adding tl0idx and set for VP8 and VP9. -// Make picture id not codec specific. +// State for setting picture id and tl0 pic idx, for VP8 and VP9 +// TODO(nisse): Make these properties not codec specific. class PayloadRouter::RtpPayloadParams final { public: RtpPayloadParams(const uint32_t ssrc, const RtpPayloadState* state) @@ -103,14 +97,42 @@ class PayloadRouter::RtpPayloadParams final { Random random(rtc::TimeMicros()); state_.picture_id = state ? state->picture_id : (random.Rand() & 0x7FFF); + state_.tl0_pic_idx = state ? state->tl0_pic_idx : (random.Rand()); } ~RtpPayloadParams() {} - void Set(RTPVideoHeader* rtp_video_header) { - if (rtp_video_header->codec == kRtpVideoVp8 && - rtp_video_header->codecHeader.VP8.pictureId != kNoPictureId) { + void Set(RTPVideoHeader* rtp_video_header, bool first_frame_in_picture) { + // Always set picture id. Set tl0_pic_idx iff temporal index is set. + if (first_frame_in_picture) { + state_.picture_id = + (static_cast(state_.picture_id) + 1) & 0x7FFF; + } + if (rtp_video_header->codec == kRtpVideoVp8) { rtp_video_header->codecHeader.VP8.pictureId = state_.picture_id; - state_.picture_id = (state_.picture_id + 1) & 0x7FFF; + + if (rtp_video_header->codecHeader.VP8.temporalIdx != kNoTemporalIdx) { + if (rtp_video_header->codecHeader.VP8.temporalIdx == 0) { + ++state_.tl0_pic_idx; + } + rtp_video_header->codecHeader.VP8.tl0PicIdx = state_.tl0_pic_idx; + } + } + if (rtp_video_header->codec == kRtpVideoVp9) { + rtp_video_header->codecHeader.VP9.picture_id = state_.picture_id; + + // Note that in the case that we have no temporal layers but we do have + // spatial layers, packets will carry layering info with a temporal_idx of + // zero, and we then have to set and increment tl0_pic_idx. + if (rtp_video_header->codecHeader.VP9.temporal_idx != kNoTemporalIdx || + rtp_video_header->codecHeader.VP9.spatial_idx != kNoSpatialIdx) { + if (first_frame_in_picture && + (rtp_video_header->codecHeader.VP9.temporal_idx == 0 || + rtp_video_header->codecHeader.VP9.temporal_idx == + kNoTemporalIdx)) { + ++state_.tl0_pic_idx; + } + rtp_video_header->codecHeader.VP9.tl0_pic_idx = state_.tl0_pic_idx; + } } } @@ -127,11 +149,7 @@ PayloadRouter::PayloadRouter(const std::vector& rtp_modules, const std::vector& ssrcs, int payload_type, const std::map& states) - : active_(false), - rtp_modules_(rtp_modules), - payload_type_(payload_type), - forced_fallback_enabled_((webrtc::field_trial::IsEnabled( - "WebRTC-VP8-Forced-Fallback-Encoder-v2"))) { + : active_(false), rtp_modules_(rtp_modules), payload_type_(payload_type) { RTC_DCHECK_EQ(ssrcs.size(), rtp_modules.size()); // SSRCs are assumed to be sorted in the same order as |rtp_modules|. for (uint32_t ssrc : ssrcs) { @@ -221,12 +239,14 @@ EncodedImageCallback::Result PayloadRouter::OnEncodedImage( int stream_index = rtp_video_header.simulcastIdx; RTC_DCHECK_LT(stream_index, rtp_modules_.size()); - if (forced_fallback_enabled_) { - // Sets picture id. The SW and HW encoder have separate picture id - // sequences, set picture id to not cause sequence discontinuties at encoder - // changes. - params_[stream_index].Set(&rtp_video_header); - } + + // Sets picture id and tl0 pic idx. + const bool first_frame_in_picture = + (codec_specific_info && codec_specific_info->codecType == kVideoCodecVP9) + ? codec_specific_info->codecSpecific.VP9.first_frame_in_picture + : true; + params_[stream_index].Set(&rtp_video_header, first_frame_in_picture); + uint32_t frame_id; if (!rtp_modules_[stream_index]->Sending()) { // The payload router could be active but this module isn't sending. diff --git a/video/payload_router.h b/video/payload_router.h index 169eda4996..e32d60729a 100644 --- a/video/payload_router.h +++ b/video/payload_router.h @@ -29,6 +29,7 @@ struct RTPVideoHeader; // Currently only VP8/VP9 specific. struct RtpPayloadState { int16_t picture_id = -1; + uint8_t tl0_pic_idx = 0; }; // PayloadRouter routes outgoing data to the correct sending RTP module, based @@ -73,7 +74,6 @@ class PayloadRouter : public EncodedImageCallback { const std::vector rtp_modules_; const int payload_type_; - const bool forced_fallback_enabled_; std::vector params_ RTC_GUARDED_BY(crit_); RTC_DISALLOW_COPY_AND_ASSIGN(PayloadRouter); diff --git a/video/payload_router_unittest.cc b/video/payload_router_unittest.cc index b193da8cda..9e09dabd7e 100644 --- a/video/payload_router_unittest.cc +++ b/video/payload_router_unittest.cc @@ -38,6 +38,8 @@ const int16_t kTl0PicIdx = 20; const uint8_t kTemporalIdx = 1; const int16_t kInitialPictureId1 = 222; const int16_t kInitialPictureId2 = 44; +const int16_t kInitialTl0PicIdx1 = 99; +const int16_t kInitialTl0PicIdx2 = 199; } // namespace TEST(PayloadRouterTest, SendOnOneModule) { @@ -307,7 +309,12 @@ TEST(PayloadRouterTest, InfoMappedToRtpVideoHeader_Vp8) { NiceMock rtp1; NiceMock rtp2; std::vector modules = {&rtp1, &rtp2}; - PayloadRouter payload_router(modules, {kSsrc1, kSsrc2}, kPayloadType, {}); + RtpPayloadState state2; + state2.picture_id = kPictureId; + state2.tl0_pic_idx = kTl0PicIdx; + std::map states = {{kSsrc2, state2}}; + + PayloadRouter payload_router(modules, {kSsrc1, kSsrc2}, kPayloadType, states); payload_router.SetActive(true); EncodedImage encoded_image; @@ -318,9 +325,8 @@ TEST(PayloadRouterTest, InfoMappedToRtpVideoHeader_Vp8) { memset(&codec_info, 0, sizeof(CodecSpecificInfo)); codec_info.codecType = kVideoCodecVP8; codec_info.codecSpecific.VP8.simulcastIdx = 1; - codec_info.codecSpecific.VP8.pictureId = kPictureId; + codec_info.codecSpecific.VP8.pictureId = -1; codec_info.codecSpecific.VP8.temporalIdx = kTemporalIdx; - codec_info.codecSpecific.VP8.tl0PicIdx = kTl0PicIdx; codec_info.codecSpecific.VP8.keyIdx = kNoKeyIdx; codec_info.codecSpecific.VP8.layerSync = true; codec_info.codecSpecific.VP8.nonReference = true; @@ -333,7 +339,7 @@ TEST(PayloadRouterTest, InfoMappedToRtpVideoHeader_Vp8) { EXPECT_EQ(VideoContentType::SCREENSHARE, header->content_type); EXPECT_EQ(1, header->simulcastIdx); EXPECT_EQ(kRtpVideoVp8, header->codec); - EXPECT_EQ(kPictureId, header->codecHeader.VP8.pictureId); + EXPECT_EQ(kPictureId + 1, header->codecHeader.VP8.pictureId); EXPECT_EQ(kTemporalIdx, header->codecHeader.VP8.temporalIdx); EXPECT_EQ(kTl0PicIdx, header->codecHeader.VP8.tl0PicIdx); EXPECT_EQ(kNoKeyIdx, header->codecHeader.VP8.keyIdx); @@ -393,8 +399,10 @@ TEST(PayloadRouterTest, CreateWithNoPreviousStates) { TEST(PayloadRouterTest, CreateWithPreviousStates) { RtpPayloadState state1; state1.picture_id = kInitialPictureId1; + state1.tl0_pic_idx = kInitialTl0PicIdx1; RtpPayloadState state2; state2.picture_id = kInitialPictureId2; + state2.tl0_pic_idx = kInitialTl0PicIdx2; std::map states = {{kSsrc1, state1}, {kSsrc2, state2}}; @@ -408,63 +416,18 @@ TEST(PayloadRouterTest, CreateWithPreviousStates) { payload_router.GetRtpPayloadStates(); EXPECT_EQ(2u, initial_states.size()); EXPECT_EQ(kInitialPictureId1, initial_states[kSsrc1].picture_id); + EXPECT_EQ(kInitialTl0PicIdx1, initial_states[kSsrc1].tl0_pic_idx); EXPECT_EQ(kInitialPictureId2, initial_states[kSsrc2].picture_id); + EXPECT_EQ(kInitialTl0PicIdx2, initial_states[kSsrc2].tl0_pic_idx); } -class PayloadRouterTest : public ::testing::Test { - public: - explicit PayloadRouterTest(const std::string& field_trials) - : override_field_trials_(field_trials) {} - virtual ~PayloadRouterTest() {} - - protected: - virtual void SetUp() { memset(&codec_info_, 0, sizeof(CodecSpecificInfo)); } - - test::ScopedFieldTrials override_field_trials_; - EncodedImage image_; - CodecSpecificInfo codec_info_; -}; - -class TestWithForcedFallbackDisabled : public PayloadRouterTest { - public: - TestWithForcedFallbackDisabled() - : PayloadRouterTest("WebRTC-VP8-Forced-Fallback-Encoder-v2/Disabled/") {} -}; - -class TestWithForcedFallbackEnabled : public PayloadRouterTest { - public: - TestWithForcedFallbackEnabled() - : PayloadRouterTest( - "WebRTC-VP8-Forced-Fallback-Encoder-v2/Enabled-1,2,3/") {} -}; - -TEST_F(TestWithForcedFallbackDisabled, PictureIdIsNotChangedForVp8) { - NiceMock rtp; - std::vector modules = {&rtp}; - PayloadRouter router(modules, {kSsrc1}, kPayloadType, {}); - router.SetActive(true); - - codec_info_.codecType = kVideoCodecVP8; - codec_info_.codecSpecific.VP8.pictureId = kPictureId; - - EXPECT_CALL(rtp, SendOutgoingData(_, _, _, _, _, _, nullptr, _, _)) - .WillOnce(Invoke([](Unused, Unused, Unused, Unused, Unused, Unused, - Unused, const RTPVideoHeader* header, Unused) { - EXPECT_EQ(kRtpVideoVp8, header->codec); - EXPECT_EQ(kPictureId, header->codecHeader.VP8.pictureId); - return true; - })); - EXPECT_CALL(rtp, Sending()).WillOnce(Return(true)); - - EXPECT_EQ(EncodedImageCallback::Result::OK, - router.OnEncodedImage(image_, &codec_info_, nullptr).error); -} - -TEST_F(TestWithForcedFallbackEnabled, PictureIdIsSetForVp8) { +TEST(PayloadRouterTest, PictureIdIsSetForVp8) { RtpPayloadState state1; state1.picture_id = kInitialPictureId1; + state1.tl0_pic_idx = kInitialTl0PicIdx1; RtpPayloadState state2; state2.picture_id = kInitialPictureId2; + state2.tl0_pic_idx = kInitialTl0PicIdx2; std::map states = {{kSsrc1, state1}, {kSsrc2, state2}}; @@ -474,119 +437,210 @@ TEST_F(TestWithForcedFallbackEnabled, PictureIdIsSetForVp8) { PayloadRouter router(modules, {kSsrc1, kSsrc2}, kPayloadType, states); router.SetActive(true); + EncodedImage encoded_image; // Modules are sending for this test. // OnEncodedImage, simulcastIdx: 0. - codec_info_.codecType = kVideoCodecVP8; - codec_info_.codecSpecific.VP8.pictureId = kPictureId; - codec_info_.codecSpecific.VP8.simulcastIdx = 0; + CodecSpecificInfo codec_info; + memset(&codec_info, 0, sizeof(CodecSpecificInfo)); + codec_info.codecType = kVideoCodecVP8; + codec_info.codecSpecific.VP8.simulcastIdx = 0; EXPECT_CALL(rtp1, SendOutgoingData(_, _, _, _, _, _, nullptr, _, _)) .WillOnce(Invoke([](Unused, Unused, Unused, Unused, Unused, Unused, Unused, const RTPVideoHeader* header, Unused) { EXPECT_EQ(kRtpVideoVp8, header->codec); - EXPECT_EQ(kInitialPictureId1, header->codecHeader.VP8.pictureId); + EXPECT_EQ(kInitialPictureId1 + 1, header->codecHeader.VP8.pictureId); return true; })); EXPECT_CALL(rtp1, Sending()).WillOnce(Return(true)); EXPECT_EQ(EncodedImageCallback::Result::OK, - router.OnEncodedImage(image_, &codec_info_, nullptr).error); + router.OnEncodedImage(encoded_image, &codec_info, nullptr).error); // OnEncodedImage, simulcastIdx: 1. - codec_info_.codecSpecific.VP8.pictureId = kPictureId; - codec_info_.codecSpecific.VP8.simulcastIdx = 1; + codec_info.codecSpecific.VP8.simulcastIdx = 1; EXPECT_CALL(rtp2, SendOutgoingData(_, _, _, _, _, _, nullptr, _, _)) .WillOnce(Invoke([](Unused, Unused, Unused, Unused, Unused, Unused, Unused, const RTPVideoHeader* header, Unused) { EXPECT_EQ(kRtpVideoVp8, header->codec); - EXPECT_EQ(kInitialPictureId2, header->codecHeader.VP8.pictureId); + EXPECT_EQ(kInitialPictureId2 + 1, header->codecHeader.VP8.pictureId); return true; })); EXPECT_CALL(rtp2, Sending()).WillOnce(Return(true)); EXPECT_EQ(EncodedImageCallback::Result::OK, - router.OnEncodedImage(image_, &codec_info_, nullptr).error); + router.OnEncodedImage(encoded_image, &codec_info, nullptr).error); - // State should hold next picture id to use. + // State should hold latest used picture id and tl0_pic_idx. states = router.GetRtpPayloadStates(); EXPECT_EQ(2u, states.size()); EXPECT_EQ(kInitialPictureId1 + 1, states[kSsrc1].picture_id); + EXPECT_EQ(kInitialTl0PicIdx1 + 1, states[kSsrc1].tl0_pic_idx); EXPECT_EQ(kInitialPictureId2 + 1, states[kSsrc2].picture_id); + EXPECT_EQ(kInitialTl0PicIdx2 + 1, states[kSsrc2].tl0_pic_idx); } -TEST_F(TestWithForcedFallbackEnabled, PictureIdWraps) { +TEST(PayloadRouterTest, PictureIdWraps) { RtpPayloadState state1; state1.picture_id = kMaxTwoBytePictureId; + state1.tl0_pic_idx = kInitialTl0PicIdx1; NiceMock rtp; std::vector modules = {&rtp}; PayloadRouter router(modules, {kSsrc1}, kPayloadType, {{kSsrc1, state1}}); router.SetActive(true); - codec_info_.codecType = kVideoCodecVP8; - codec_info_.codecSpecific.VP8.pictureId = kPictureId; + EncodedImage encoded_image; + CodecSpecificInfo codec_info; + memset(&codec_info, 0, sizeof(CodecSpecificInfo)); + codec_info.codecType = kVideoCodecVP8; + codec_info.codecSpecific.VP8.temporalIdx = kNoTemporalIdx; EXPECT_CALL(rtp, SendOutgoingData(_, _, _, _, _, _, nullptr, _, _)) .WillOnce(Invoke([](Unused, Unused, Unused, Unused, Unused, Unused, Unused, const RTPVideoHeader* header, Unused) { EXPECT_EQ(kRtpVideoVp8, header->codec); - EXPECT_EQ(kMaxTwoBytePictureId, header->codecHeader.VP8.pictureId); + EXPECT_EQ(0, header->codecHeader.VP8.pictureId); return true; })); EXPECT_CALL(rtp, Sending()).WillOnce(Return(true)); EXPECT_EQ(EncodedImageCallback::Result::OK, - router.OnEncodedImage(image_, &codec_info_, nullptr).error); + router.OnEncodedImage(encoded_image, &codec_info, nullptr).error); - // State should hold next picture id to use. + // State should hold latest used picture id and tl0_pic_idx. std::map states = router.GetRtpPayloadStates(); EXPECT_EQ(1u, states.size()); EXPECT_EQ(0, states[kSsrc1].picture_id); // Wrapped. + EXPECT_EQ(kInitialTl0PicIdx1, states[kSsrc1].tl0_pic_idx); } -TEST_F(TestWithForcedFallbackEnabled, PictureIdIsNotSetIfNoPictureId) { +TEST(PayloadRouterTest, Tl0PicIdxUpdatedForVp8) { + RtpPayloadState state; + state.picture_id = kInitialPictureId1; + state.tl0_pic_idx = kInitialTl0PicIdx1; + std::map states = {{kSsrc1, state}}; + NiceMock rtp; std::vector modules = {&rtp}; - PayloadRouter router(modules, {kSsrc1}, kPayloadType, {}); + PayloadRouter router(modules, {kSsrc1}, kPayloadType, states); router.SetActive(true); - codec_info_.codecType = kVideoCodecVP8; - codec_info_.codecSpecific.VP8.pictureId = kNoPictureId; + EncodedImage encoded_image; + // Modules are sending for this test. + // OnEncodedImage, temporalIdx: 1. + CodecSpecificInfo codec_info; + memset(&codec_info, 0, sizeof(CodecSpecificInfo)); + codec_info.codecType = kVideoCodecVP8; + codec_info.codecSpecific.VP8.temporalIdx = 1; EXPECT_CALL(rtp, SendOutgoingData(_, _, _, _, _, _, nullptr, _, _)) .WillOnce(Invoke([](Unused, Unused, Unused, Unused, Unused, Unused, Unused, const RTPVideoHeader* header, Unused) { EXPECT_EQ(kRtpVideoVp8, header->codec); - EXPECT_EQ(kNoPictureId, header->codecHeader.VP8.pictureId); + EXPECT_EQ(kInitialPictureId1 + 1, header->codecHeader.VP8.pictureId); + EXPECT_EQ(kInitialTl0PicIdx1, header->codecHeader.VP8.tl0PicIdx); return true; })); EXPECT_CALL(rtp, Sending()).WillOnce(Return(true)); EXPECT_EQ(EncodedImageCallback::Result::OK, - router.OnEncodedImage(image_, &codec_info_, nullptr).error); + router.OnEncodedImage(encoded_image, &codec_info, nullptr).error); + + // OnEncodedImage, temporalIdx: 0. + codec_info.codecSpecific.VP8.temporalIdx = 0; + + EXPECT_CALL(rtp, SendOutgoingData(_, _, _, _, _, _, nullptr, _, _)) + .WillOnce(Invoke([](Unused, Unused, Unused, Unused, Unused, Unused, + Unused, const RTPVideoHeader* header, Unused) { + EXPECT_EQ(kRtpVideoVp8, header->codec); + EXPECT_EQ(kInitialPictureId1 + 2, header->codecHeader.VP8.pictureId); + EXPECT_EQ(kInitialTl0PicIdx1 + 1, header->codecHeader.VP8.tl0PicIdx); + return true; + })); + EXPECT_CALL(rtp, Sending()).WillOnce(Return(true)); + + EXPECT_EQ(EncodedImageCallback::Result::OK, + router.OnEncodedImage(encoded_image, &codec_info, nullptr).error); + + // State should hold latest used picture id and tl0_pic_idx. + states = router.GetRtpPayloadStates(); + EXPECT_EQ(1u, states.size()); + EXPECT_EQ(kInitialPictureId1 + 2, states[kSsrc1].picture_id); + EXPECT_EQ(kInitialTl0PicIdx1 + 1, states[kSsrc1].tl0_pic_idx); } -TEST_F(TestWithForcedFallbackEnabled, PictureIdIsNotSetForVp9) { +TEST(PayloadRouterTest, Tl0PicIdxUpdatedForVp9) { + RtpPayloadState state; + state.picture_id = kInitialPictureId1; + state.tl0_pic_idx = kInitialTl0PicIdx1; + std::map states = {{kSsrc1, state}}; + NiceMock rtp; std::vector modules = {&rtp}; - PayloadRouter router(modules, {kSsrc1}, kPayloadType, {}); + PayloadRouter router(modules, {kSsrc1}, kPayloadType, states); router.SetActive(true); - codec_info_.codecType = kVideoCodecVP9; - codec_info_.codecSpecific.VP9.picture_id = kPictureId; + EncodedImage encoded_image; + // Modules are sending for this test. + // OnEncodedImage, temporalIdx: 1. + CodecSpecificInfo codec_info; + memset(&codec_info, 0, sizeof(CodecSpecificInfo)); + codec_info.codecType = kVideoCodecVP9; + codec_info.codecSpecific.VP9.temporal_idx = 1; + codec_info.codecSpecific.VP9.first_frame_in_picture = true; EXPECT_CALL(rtp, SendOutgoingData(_, _, _, _, _, _, nullptr, _, _)) .WillOnce(Invoke([](Unused, Unused, Unused, Unused, Unused, Unused, Unused, const RTPVideoHeader* header, Unused) { EXPECT_EQ(kRtpVideoVp9, header->codec); - EXPECT_EQ(kPictureId, header->codecHeader.VP9.picture_id); + EXPECT_EQ(kInitialPictureId1 + 1, header->codecHeader.VP9.picture_id); + EXPECT_EQ(kInitialTl0PicIdx1, header->codecHeader.VP9.tl0_pic_idx); return true; })); EXPECT_CALL(rtp, Sending()).WillOnce(Return(true)); EXPECT_EQ(EncodedImageCallback::Result::OK, - router.OnEncodedImage(image_, &codec_info_, nullptr).error); + router.OnEncodedImage(encoded_image, &codec_info, nullptr).error); + + // OnEncodedImage, temporalIdx: 0. + codec_info.codecSpecific.VP9.temporal_idx = 0; + + EXPECT_CALL(rtp, SendOutgoingData(_, _, _, _, _, _, nullptr, _, _)) + .WillOnce(Invoke([](Unused, Unused, Unused, Unused, Unused, Unused, + Unused, const RTPVideoHeader* header, Unused) { + EXPECT_EQ(kRtpVideoVp9, header->codec); + EXPECT_EQ(kInitialPictureId1 + 2, header->codecHeader.VP9.picture_id); + EXPECT_EQ(kInitialTl0PicIdx1 + 1, header->codecHeader.VP9.tl0_pic_idx); + return true; + })); + EXPECT_CALL(rtp, Sending()).WillOnce(Return(true)); + + EXPECT_EQ(EncodedImageCallback::Result::OK, + router.OnEncodedImage(encoded_image, &codec_info, nullptr).error); + + // OnEncodedImage, first_frame_in_picture = false + codec_info.codecSpecific.VP9.first_frame_in_picture = false; + + EXPECT_CALL(rtp, SendOutgoingData(_, _, _, _, _, _, nullptr, _, _)) + .WillOnce(Invoke([](Unused, Unused, Unused, Unused, Unused, Unused, + Unused, const RTPVideoHeader* header, Unused) { + EXPECT_EQ(kRtpVideoVp9, header->codec); + EXPECT_EQ(kInitialPictureId1 + 2, header->codecHeader.VP9.picture_id); + EXPECT_EQ(kInitialTl0PicIdx1 + 1, header->codecHeader.VP9.tl0_pic_idx); + return true; + })); + EXPECT_CALL(rtp, Sending()).WillOnce(Return(true)); + + EXPECT_EQ(EncodedImageCallback::Result::OK, + router.OnEncodedImage(encoded_image, &codec_info, nullptr).error); + + // State should hold latest used picture id and tl0_pic_idx. + states = router.GetRtpPayloadStates(); + EXPECT_EQ(1u, states.size()); + EXPECT_EQ(kInitialPictureId1 + 2, states[kSsrc1].picture_id); + EXPECT_EQ(kInitialTl0PicIdx1 + 1, states[kSsrc1].tl0_pic_idx); } } // namespace webrtc