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}
This commit is contained in:
Niels Möller
2018-03-15 12:28:53 +01:00
committed by Commit Bot
parent 9f64b9c6fe
commit bb894ffcb4
5 changed files with 187 additions and 106 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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<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 @@ PayloadRouter::PayloadRouter(const std::vector<RtpRtcp*>& rtp_modules,
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 @@ 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.

View File

@ -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<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);

View File

@ -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<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 @@ 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<uint32_t, RtpPayloadState> 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<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 @@ 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<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