Make PayloadRouter own the picture id and tl0 pic idx sequences.
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 <nisse@webrtc.org>
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22448}diff --git a/modules/video_coding/codecs/vp9/vp9_impl.cc b/modules/video_coding/codecs/vp9/vp9_impl.cc
index c7c6247..06f485b 100644
--- a/modules/video_coding/codecs/vp9/vp9_impl.cc
+++ b/modules/video_coding/codecs/vp9/vp9_impl.cc
@@ -646,6 +646,8 @@
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 6616053..7524631 100644
--- a/modules/video_coding/include/video_codec_interface.h
+++ b/modules/video_coding/include/video_codec_interface.h
@@ -28,23 +28,28 @@
// 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 4ecb620..f980bc4 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 @@
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 @@
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 @@
} // 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 @@
Random random(rtc::TimeMicros());
state_.picture_id =
state ? state->picture_id : (random.Rand<int16_t>() & 0x7FFF);
+ state_.tl0_pic_idx = state ? state->tl0_pic_idx : (random.Rand<uint8_t>());
}
~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<uint16_t>(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 @@
const std::vector<uint32_t>& ssrcs,
int payload_type,
const std::map<uint32_t, RtpPayloadState>& 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 @@
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 169eda4..e32d607 100644
--- a/video/payload_router.h
+++ b/video/payload_router.h
@@ -29,6 +29,7 @@
// 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 @@
const std::vector<RtpRtcp*> rtp_modules_;
const int payload_type_;
- const bool forced_fallback_enabled_;
std::vector<RtpPayloadParams> 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 b193da8..9e09dab 100644
--- a/video/payload_router_unittest.cc
+++ b/video/payload_router_unittest.cc
@@ -38,6 +38,8 @@
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 @@
NiceMock<MockRtpRtcp> rtp1;
NiceMock<MockRtpRtcp> rtp2;
std::vector<RtpRtcp*> modules = {&rtp1, &rtp2};
- PayloadRouter payload_router(modules, {kSsrc1, kSsrc2}, kPayloadType, {});
+ RtpPayloadState state2;
+ state2.picture_id = kPictureId;
+ state2.tl0_pic_idx = kTl0PicIdx;
+ std::map<uint32_t, RtpPayloadState> states = {{kSsrc2, state2}};
+
+ PayloadRouter payload_router(modules, {kSsrc1, kSsrc2}, kPayloadType, states);
payload_router.SetActive(true);
EncodedImage encoded_image;
@@ -318,9 +325,8 @@
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 @@
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, 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<uint32_t, RtpPayloadState> states = {{kSsrc1, state1},
{kSsrc2, state2}};
@@ -408,63 +416,18 @@
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<MockRtpRtcp> rtp;
- std::vector<RtpRtcp*> 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<uint32_t, RtpPayloadState> states = {{kSsrc1, state1},
{kSsrc2, state2}};
@@ -474,119 +437,210 @@
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<MockRtpRtcp> rtp;
std::vector<RtpRtcp*> 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<uint32_t, RtpPayloadState> 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<uint32_t, RtpPayloadState> states = {{kSsrc1, state}};
+
NiceMock<MockRtpRtcp> rtp;
std::vector<RtpRtcp*> 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<uint32_t, RtpPayloadState> states = {{kSsrc1, state}};
+
NiceMock<MockRtpRtcp> rtp;
std::vector<RtpRtcp*> 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