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:
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user