Fixes issue triggered by WebRTC-VP9-PerformanceFlags trial.
Using WebRTC-VP9-PerformanceFlags and settings a multi-layer config, and then configuring the codec in non-svc mode would cause us to not set the cpu speed in libvpx. For some reason, that could trigger a crash in the encoder. This CL fixes that, and adds new test coverage for the code affected byt the trial. Bug: chromium:1167353, webrtc:11551 Change-Id: Iddb92fe03fc12bac37717908a8b5df4f3d411bf2 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/202761 Commit-Queue: Erik Språng <sprang@webrtc.org> Reviewed-by: Danil Chapovalov <danilchap@webrtc.org> Cr-Commit-Position: refs/heads/master@{#33051}
This commit is contained in:
@ -760,7 +760,7 @@ int LibvpxVp9Encoder::InitAndSetControlSettings(const VideoCodec* inst) {
|
||||
libvpx_->codec_control(encoder_, VP9E_SET_SVC, 1);
|
||||
libvpx_->codec_control(encoder_, VP9E_SET_SVC_PARAMETERS, &svc_params_);
|
||||
}
|
||||
if (!performance_flags_.use_per_layer_speed) {
|
||||
if (!is_svc_ || !performance_flags_.use_per_layer_speed) {
|
||||
libvpx_->codec_control(
|
||||
encoder_, VP8E_SET_CPUUSED,
|
||||
performance_flags_by_spatial_index_.rbegin()->base_layer_speed);
|
||||
|
||||
@ -8,18 +8,25 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "api/test/create_frame_generator.h"
|
||||
#include "api/test/frame_generator_interface.h"
|
||||
#include "api/test/mock_video_encoder.h"
|
||||
#include "api/video/color_space.h"
|
||||
#include "api/video/i420_buffer.h"
|
||||
#include "api/video_codecs/video_encoder.h"
|
||||
#include "common_video/libyuv/include/webrtc_libyuv.h"
|
||||
#include "media/base/vp9_profile.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "modules/video_coding/codecs/interface/libvpx_interface.h"
|
||||
#include "modules/video_coding/codecs/interface/mock_libvpx_interface.h"
|
||||
#include "modules/video_coding/codecs/test/encoded_video_frame_producer.h"
|
||||
#include "modules/video_coding/codecs/test/video_codec_unittest.h"
|
||||
#include "modules/video_coding/codecs/vp9/include/vp9.h"
|
||||
#include "modules/video_coding/codecs/vp9/libvpx_vp9_encoder.h"
|
||||
#include "modules/video_coding/codecs/vp9/svc_config.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
#include "test/explicit_key_value_config.h"
|
||||
#include "test/field_trial.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
@ -28,11 +35,28 @@
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::A;
|
||||
using ::testing::AllOf;
|
||||
using ::testing::An;
|
||||
using ::testing::AnyNumber;
|
||||
using ::testing::ByRef;
|
||||
using ::testing::DoAll;
|
||||
using ::testing::Each;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::ElementsAreArray;
|
||||
using ::testing::Field;
|
||||
using ::testing::IsEmpty;
|
||||
using ::testing::Mock;
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::Return;
|
||||
using ::testing::SafeMatcherCast;
|
||||
using ::testing::SaveArgPointee;
|
||||
using ::testing::SetArgPointee;
|
||||
using ::testing::SizeIs;
|
||||
using ::testing::TypedEq;
|
||||
using ::testing::UnorderedElementsAreArray;
|
||||
using ::testing::WithArg;
|
||||
using EncoderInfo = webrtc::VideoEncoder::EncoderInfo;
|
||||
using FramerateFractions =
|
||||
absl::InlinedVector<uint8_t, webrtc::kMaxTemporalStreams>;
|
||||
@ -1644,7 +1668,7 @@ TEST_F(TestVp9Impl, EncoderInfoFpsAllocation) {
|
||||
expected_fps_allocation[1] = expected_fps_allocation[0];
|
||||
expected_fps_allocation[2] = expected_fps_allocation[0];
|
||||
EXPECT_THAT(encoder_->GetEncoderInfo().fps_allocation,
|
||||
::testing::ElementsAreArray(expected_fps_allocation));
|
||||
ElementsAreArray(expected_fps_allocation));
|
||||
}
|
||||
|
||||
TEST_F(TestVp9Impl, EncoderInfoFpsAllocationFlexibleMode) {
|
||||
@ -2057,4 +2081,288 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
[](const auto& info) {
|
||||
return test::FrameGeneratorInterface::OutputTypeToString(info.param);
|
||||
});
|
||||
|
||||
// Helper function to populate an vpx_image_t instance with dimensions and
|
||||
// potential image data.
|
||||
std::function<vpx_image_t*(vpx_image_t*,
|
||||
vpx_img_fmt_t,
|
||||
unsigned int,
|
||||
unsigned int,
|
||||
unsigned int,
|
||||
unsigned char* img_data)>
|
||||
GetWrapImageFunction(vpx_image_t* img) {
|
||||
return [img](vpx_image_t* /*img*/, vpx_img_fmt_t fmt, unsigned int d_w,
|
||||
unsigned int d_h, unsigned int /*stride_align*/,
|
||||
unsigned char* img_data) {
|
||||
img->fmt = fmt;
|
||||
img->d_w = d_w;
|
||||
img->d_h = d_h;
|
||||
img->img_data = img_data;
|
||||
return img;
|
||||
};
|
||||
}
|
||||
|
||||
TEST(Vp9SpeedSettingsTrialsTest, SvcExtraCfgNotPopulatedByDefault) {
|
||||
test::ExplicitKeyValueConfig trials("");
|
||||
|
||||
// Keep a raw pointer for EXPECT calls and the like. Ownership is otherwise
|
||||
// passed on to LibvpxVp9Encoder.
|
||||
auto* const vpx = new NiceMock<MockLibvpxInterface>();
|
||||
LibvpxVp9Encoder encoder(cricket::VideoCodec(),
|
||||
absl::WrapUnique<LibvpxInterface>(vpx), trials);
|
||||
|
||||
VideoCodec settings = DefaultCodecSettings();
|
||||
// Configure 3 spatial and three temporal ayers.
|
||||
ConfigureSvc(settings, 3, 3);
|
||||
vpx_image_t img;
|
||||
|
||||
ON_CALL(*vpx, img_wrap).WillByDefault(GetWrapImageFunction(&img));
|
||||
ON_CALL(*vpx, codec_enc_config_default)
|
||||
.WillByDefault(DoAll(WithArg<1>([](vpx_codec_enc_cfg_t* cfg) {
|
||||
memset(cfg, 0, sizeof(vpx_codec_enc_cfg_t));
|
||||
}),
|
||||
Return(VPX_CODEC_OK)));
|
||||
EXPECT_CALL(*vpx,
|
||||
codec_control(
|
||||
_, VP9E_SET_SVC_PARAMETERS,
|
||||
SafeMatcherCast<vpx_svc_extra_cfg_t*>(AllOf(
|
||||
Field(&vpx_svc_extra_cfg_t::speed_per_layer, Each(0)),
|
||||
Field(&vpx_svc_extra_cfg_t::loopfilter_ctrl, Each(0))))));
|
||||
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder.InitEncode(&settings, kSettings));
|
||||
}
|
||||
|
||||
TEST(Vp9SpeedSettingsTrialsTest, NoSvcUsesGlobalSpeedFromTl0InLayerConfig) {
|
||||
// TL0 speed 8 at >= 480x270, 5 if below that.
|
||||
test::ExplicitKeyValueConfig trials(
|
||||
"WebRTC-VP9-PerformanceFlags/"
|
||||
"use_per_layer_speed,"
|
||||
"min_pixel_count:0|129600,"
|
||||
"base_layer_speed:4|8,"
|
||||
"high_layer_speed:5|9,"
|
||||
"deblock_mode:1|0/");
|
||||
|
||||
// Keep a raw pointer for EXPECT calls and the like. Ownership is otherwise
|
||||
// passed on to LibvpxVp9Encoder.
|
||||
auto* const vpx = new NiceMock<MockLibvpxInterface>();
|
||||
LibvpxVp9Encoder encoder(cricket::VideoCodec(),
|
||||
absl::WrapUnique<LibvpxInterface>(vpx), trials);
|
||||
|
||||
VideoCodec settings = DefaultCodecSettings();
|
||||
settings.width = 480;
|
||||
settings.height = 270;
|
||||
vpx_image_t img;
|
||||
|
||||
ON_CALL(*vpx, img_wrap).WillByDefault(GetWrapImageFunction(&img));
|
||||
ON_CALL(*vpx, codec_enc_config_default)
|
||||
.WillByDefault(DoAll(WithArg<1>([](vpx_codec_enc_cfg_t* cfg) {
|
||||
memset(cfg, 0, sizeof(vpx_codec_enc_cfg_t));
|
||||
}),
|
||||
Return(VPX_CODEC_OK)));
|
||||
EXPECT_CALL(*vpx, codec_control(_, _, An<int>())).Times(AnyNumber());
|
||||
|
||||
EXPECT_CALL(*vpx, codec_control(_, VP9E_SET_SVC_PARAMETERS,
|
||||
A<vpx_svc_extra_cfg_t*>()))
|
||||
.Times(0);
|
||||
|
||||
EXPECT_CALL(*vpx, codec_control(_, VP8E_SET_CPUUSED, TypedEq<int>(8)));
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder.InitEncode(&settings, kSettings));
|
||||
|
||||
encoder.Release();
|
||||
settings.width = 352;
|
||||
settings.height = 216;
|
||||
|
||||
EXPECT_CALL(*vpx, codec_control(_, VP8E_SET_CPUUSED, TypedEq<int>(4)));
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder.InitEncode(&settings, kSettings));
|
||||
}
|
||||
|
||||
TEST(Vp9SpeedSettingsTrialsTest,
|
||||
NoPerLayerFlagUsesGlobalSpeedFromTopLayerInConfig) {
|
||||
// TL0 speed 8 at >= 480x270, 5 if below that.
|
||||
test::ExplicitKeyValueConfig trials(
|
||||
"WebRTC-VP9-PerformanceFlags/"
|
||||
"min_pixel_count:0|129600,"
|
||||
"base_layer_speed:4|8,"
|
||||
"high_layer_speed:5|9,"
|
||||
"deblock_mode:1|0/");
|
||||
|
||||
// Keep a raw pointer for EXPECT calls and the like. Ownership is otherwise
|
||||
// passed on to LibvpxVp9Encoder.
|
||||
auto* const vpx = new NiceMock<MockLibvpxInterface>();
|
||||
LibvpxVp9Encoder encoder(cricket::VideoCodec(),
|
||||
absl::WrapUnique<LibvpxInterface>(vpx), trials);
|
||||
|
||||
VideoCodec settings = DefaultCodecSettings();
|
||||
settings.width = 480;
|
||||
settings.height = 270;
|
||||
ConfigureSvc(settings, 2, 3);
|
||||
vpx_image_t img;
|
||||
|
||||
ON_CALL(*vpx, img_wrap).WillByDefault(GetWrapImageFunction(&img));
|
||||
ON_CALL(*vpx, codec_enc_config_default)
|
||||
.WillByDefault(DoAll(WithArg<1>([](vpx_codec_enc_cfg_t* cfg) {
|
||||
memset(cfg, 0, sizeof(vpx_codec_enc_cfg_t));
|
||||
}),
|
||||
Return(VPX_CODEC_OK)));
|
||||
EXPECT_CALL(*vpx, codec_control(_, _, An<int>())).Times(AnyNumber());
|
||||
|
||||
// Speed settings not populated when 'use_per_layer_speed' flag is absent.
|
||||
EXPECT_CALL(*vpx,
|
||||
codec_control(
|
||||
_, VP9E_SET_SVC_PARAMETERS,
|
||||
SafeMatcherCast<vpx_svc_extra_cfg_t*>(AllOf(
|
||||
Field(&vpx_svc_extra_cfg_t::speed_per_layer, Each(0)),
|
||||
Field(&vpx_svc_extra_cfg_t::loopfilter_ctrl, Each(0))))))
|
||||
.Times(2);
|
||||
|
||||
EXPECT_CALL(*vpx, codec_control(_, VP8E_SET_CPUUSED, TypedEq<int>(8)));
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder.InitEncode(&settings, kSettings));
|
||||
|
||||
encoder.Release();
|
||||
settings.width = 476;
|
||||
settings.height = 268;
|
||||
settings.spatialLayers[0].width = settings.width / 2;
|
||||
settings.spatialLayers[0].height = settings.height / 2;
|
||||
settings.spatialLayers[1].width = settings.width;
|
||||
settings.spatialLayers[1].height = settings.height;
|
||||
|
||||
EXPECT_CALL(*vpx, codec_control(_, VP8E_SET_CPUUSED, TypedEq<int>(4)));
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder.InitEncode(&settings, kSettings));
|
||||
}
|
||||
|
||||
TEST(Vp9SpeedSettingsTrialsTest, PerLayerFlagsWithSvc) {
|
||||
// Per-temporal and satial layer speed settings:
|
||||
// SL0: TL0 = speed 5, TL1/TL2 = speed 8.
|
||||
// SL1/2: TL0 = speed 7, TL1/TL2 = speed 9.
|
||||
// Deblocking-mode per spatial layer:
|
||||
// SL0: mode 1, SL1/2: mode 0.
|
||||
test::ExplicitKeyValueConfig trials(
|
||||
"WebRTC-VP9-PerformanceFlags/"
|
||||
"use_per_layer_speed,"
|
||||
"min_pixel_count:0|129600,"
|
||||
"base_layer_speed:5|7,"
|
||||
"high_layer_speed:8|9,"
|
||||
"deblock_mode:1|0/");
|
||||
|
||||
// Keep a raw pointer for EXPECT calls and the like. Ownership is otherwise
|
||||
// passed on to LibvpxVp9Encoder.
|
||||
auto* const vpx = new NiceMock<MockLibvpxInterface>();
|
||||
LibvpxVp9Encoder encoder(cricket::VideoCodec(),
|
||||
absl::WrapUnique<LibvpxInterface>(vpx), trials);
|
||||
|
||||
VideoCodec settings = DefaultCodecSettings();
|
||||
const int kNumSpatialLayers = 3;
|
||||
ConfigureSvc(settings, kNumSpatialLayers, /*num_temporal_layers=*/3);
|
||||
vpx_image_t img;
|
||||
|
||||
// Speed settings per spatial layer, for TL0.
|
||||
const int kBaseTlSpeed[VPX_MAX_LAYERS] = {5, 7, 7};
|
||||
// Speed settings per spatial layer, for TL1, TL2.
|
||||
const int kHighTlSpeed[VPX_MAX_LAYERS] = {8, 9, 9};
|
||||
// Loopfilter settings are handled within libvpx, so this array is valid for
|
||||
// both TL0 and higher.
|
||||
const int kLoopFilter[VPX_MAX_LAYERS] = {1, 0, 0};
|
||||
|
||||
ON_CALL(*vpx, img_wrap).WillByDefault(GetWrapImageFunction(&img));
|
||||
ON_CALL(*vpx, codec_enc_config_default)
|
||||
.WillByDefault(DoAll(WithArg<1>([](vpx_codec_enc_cfg_t* cfg) {
|
||||
memset(cfg, 0, sizeof(vpx_codec_enc_cfg_t));
|
||||
}),
|
||||
Return(VPX_CODEC_OK)));
|
||||
EXPECT_CALL(
|
||||
*vpx, codec_control(_, VP9E_SET_SVC_PARAMETERS,
|
||||
SafeMatcherCast<vpx_svc_extra_cfg_t*>(
|
||||
AllOf(Field(&vpx_svc_extra_cfg_t::speed_per_layer,
|
||||
ElementsAreArray(kBaseTlSpeed)),
|
||||
Field(&vpx_svc_extra_cfg_t::loopfilter_ctrl,
|
||||
ElementsAreArray(kLoopFilter))))));
|
||||
|
||||
// Capture the callback into the vp9 wrapper.
|
||||
vpx_codec_priv_output_cx_pkt_cb_pair_t callback_pointer = {};
|
||||
EXPECT_CALL(*vpx, codec_control(_, VP9E_REGISTER_CX_CALLBACK, A<void*>()))
|
||||
.WillOnce(WithArg<2>([&](void* cbp) {
|
||||
callback_pointer =
|
||||
*reinterpret_cast<vpx_codec_priv_output_cx_pkt_cb_pair_t*>(cbp);
|
||||
return VPX_CODEC_OK;
|
||||
}));
|
||||
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder.InitEncode(&settings, kSettings));
|
||||
|
||||
MockEncodedImageCallback callback;
|
||||
encoder.RegisterEncodeCompleteCallback(&callback);
|
||||
auto frame_generator = test::CreateSquareFrameGenerator(
|
||||
kWidth, kHeight, test::FrameGeneratorInterface::OutputType::kI420, 10);
|
||||
Mock::VerifyAndClearExpectations(vpx);
|
||||
|
||||
uint8_t data[1] = {0};
|
||||
vpx_codec_cx_pkt encoded_data = {};
|
||||
encoded_data.data.frame.buf = &data;
|
||||
encoded_data.data.frame.sz = 1;
|
||||
|
||||
const auto kImageOk =
|
||||
EncodedImageCallback::Result(EncodedImageCallback::Result::OK);
|
||||
|
||||
int spatial_id = 0;
|
||||
int temporal_id = 0;
|
||||
EXPECT_CALL(*vpx,
|
||||
codec_control(_, VP9E_SET_SVC_LAYER_ID, A<vpx_svc_layer_id_t*>()))
|
||||
.Times(AnyNumber());
|
||||
EXPECT_CALL(*vpx,
|
||||
codec_control(_, VP9E_GET_SVC_LAYER_ID, A<vpx_svc_layer_id_t*>()))
|
||||
.WillRepeatedly(WithArg<2>([&](vpx_svc_layer_id_t* layer_id) {
|
||||
layer_id->spatial_layer_id = spatial_id;
|
||||
layer_id->temporal_layer_id = temporal_id;
|
||||
return VPX_CODEC_OK;
|
||||
}));
|
||||
vpx_svc_ref_frame_config_t stored_refs = {};
|
||||
ON_CALL(*vpx, codec_control(_, VP9E_SET_SVC_REF_FRAME_CONFIG,
|
||||
A<vpx_svc_ref_frame_config_t*>()))
|
||||
.WillByDefault(
|
||||
DoAll(SaveArgPointee<2>(&stored_refs), Return(VPX_CODEC_OK)));
|
||||
ON_CALL(*vpx, codec_control(_, VP9E_GET_SVC_REF_FRAME_CONFIG,
|
||||
A<vpx_svc_ref_frame_config_t*>()))
|
||||
.WillByDefault(
|
||||
DoAll(SetArgPointee<2>(ByRef(stored_refs)), Return(VPX_CODEC_OK)));
|
||||
|
||||
// First frame is keyframe.
|
||||
encoded_data.data.frame.flags = VPX_FRAME_IS_KEY;
|
||||
|
||||
// Default 3-layer temporal pattern: 0-2-1-2, then repeat and do two more.
|
||||
for (int ti : {0, 2, 1, 2, 0, 2}) {
|
||||
EXPECT_CALL(*vpx, codec_encode).WillOnce(Return(VPX_CODEC_OK));
|
||||
// No update expected if flags haven't changed, and they change we we move
|
||||
// between base temporal layer and non-base temporal layer.
|
||||
if ((ti > 0) != (temporal_id > 0)) {
|
||||
EXPECT_CALL(*vpx, codec_control(
|
||||
_, VP9E_SET_SVC_PARAMETERS,
|
||||
SafeMatcherCast<vpx_svc_extra_cfg_t*>(AllOf(
|
||||
Field(&vpx_svc_extra_cfg_t::speed_per_layer,
|
||||
ElementsAreArray(ti == 0 ? kBaseTlSpeed
|
||||
: kHighTlSpeed)),
|
||||
Field(&vpx_svc_extra_cfg_t::loopfilter_ctrl,
|
||||
ElementsAreArray(kLoopFilter))))));
|
||||
} else {
|
||||
EXPECT_CALL(*vpx, codec_control(_, VP9E_SET_SVC_PARAMETERS,
|
||||
A<vpx_svc_extra_cfg_t*>()))
|
||||
.Times(0);
|
||||
}
|
||||
|
||||
VideoFrame frame =
|
||||
VideoFrame::Builder()
|
||||
.set_video_frame_buffer(frame_generator->NextFrame().buffer)
|
||||
.build();
|
||||
encoder.Encode(frame, nullptr);
|
||||
|
||||
temporal_id = ti;
|
||||
for (int si = 0; si < kNumSpatialLayers; ++si) {
|
||||
spatial_id = si;
|
||||
|
||||
EXPECT_CALL(callback, OnEncodedImage).WillOnce(Return(kImageOk));
|
||||
callback_pointer.output_cx_pkt(&encoded_data, callback_pointer.user_priv);
|
||||
}
|
||||
|
||||
encoded_data.data.frame.flags = 0; // Following frames are delta frames.
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
Reference in New Issue
Block a user