blob: 2e436a91fcac3dc049b0c870afdddbe9a2ddd53a [file] [log] [blame]
/*
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_SIMULCAST_UNITTEST_H_
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_SIMULCAST_UNITTEST_H_
#include <algorithm>
#include <vector>
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/common.h"
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
#include "webrtc/experiments.h"
#include "webrtc/modules/video_coding/codecs/interface/mock/mock_video_codec_interface.h"
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h"
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
#include "webrtc/video_frame.h"
#include "gtest/gtest.h"
using ::testing::_;
using ::testing::AllOf;
using ::testing::Field;
using ::testing::Return;
namespace webrtc {
namespace testing {
const int kDefaultWidth = 1280;
const int kDefaultHeight = 720;
const int kNumberOfSimulcastStreams = 3;
const int kColorY = 66;
const int kColorU = 22;
const int kColorV = 33;
const int kMaxBitrates[kNumberOfSimulcastStreams] = {150, 600, 1200};
const int kMinBitrates[kNumberOfSimulcastStreams] = {50, 150, 600};
const int kTargetBitrates[kNumberOfSimulcastStreams] = {100, 450, 1000};
const int kDefaultTemporalLayerProfile[3] = {3, 3, 3};
template<typename T> void SetExpectedValues3(T value0,
T value1,
T value2,
T* expected_values) {
expected_values[0] = value0;
expected_values[1] = value1;
expected_values[2] = value2;
}
class Vp8TestEncodedImageCallback : public EncodedImageCallback {
public:
Vp8TestEncodedImageCallback()
: picture_id_(-1) {
memset(temporal_layer_, -1, sizeof(temporal_layer_));
memset(layer_sync_, false, sizeof(layer_sync_));
}
~Vp8TestEncodedImageCallback() {
delete [] encoded_key_frame_._buffer;
delete [] encoded_frame_._buffer;
}
virtual int32_t Encoded(const EncodedImage& encoded_image,
const CodecSpecificInfo* codec_specific_info,
const RTPFragmentationHeader* fragmentation) {
// Only store the base layer.
if (codec_specific_info->codecSpecific.VP8.simulcastIdx == 0) {
if (encoded_image._frameType == kKeyFrame) {
delete [] encoded_key_frame_._buffer;
encoded_key_frame_._buffer = new uint8_t[encoded_image._size];
encoded_key_frame_._size = encoded_image._size;
encoded_key_frame_._length = encoded_image._length;
encoded_key_frame_._frameType = kKeyFrame;
encoded_key_frame_._completeFrame = encoded_image._completeFrame;
memcpy(encoded_key_frame_._buffer,
encoded_image._buffer,
encoded_image._length);
} else {
delete [] encoded_frame_._buffer;
encoded_frame_._buffer = new uint8_t[encoded_image._size];
encoded_frame_._size = encoded_image._size;
encoded_frame_._length = encoded_image._length;
memcpy(encoded_frame_._buffer,
encoded_image._buffer,
encoded_image._length);
}
}
picture_id_ = codec_specific_info->codecSpecific.VP8.pictureId;
layer_sync_[codec_specific_info->codecSpecific.VP8.simulcastIdx] =
codec_specific_info->codecSpecific.VP8.layerSync;
temporal_layer_[codec_specific_info->codecSpecific.VP8.simulcastIdx] =
codec_specific_info->codecSpecific.VP8.temporalIdx;
return 0;
}
void GetLastEncodedFrameInfo(int* picture_id, int* temporal_layer,
bool* layer_sync, int stream) {
*picture_id = picture_id_;
*temporal_layer = temporal_layer_[stream];
*layer_sync = layer_sync_[stream];
}
void GetLastEncodedKeyFrame(EncodedImage* encoded_key_frame) {
*encoded_key_frame = encoded_key_frame_;
}
void GetLastEncodedFrame(EncodedImage* encoded_frame) {
*encoded_frame = encoded_frame_;
}
private:
EncodedImage encoded_key_frame_;
EncodedImage encoded_frame_;
int picture_id_;
int temporal_layer_[kNumberOfSimulcastStreams];
bool layer_sync_[kNumberOfSimulcastStreams];
};
class Vp8TestDecodedImageCallback : public DecodedImageCallback {
public:
Vp8TestDecodedImageCallback()
: decoded_frames_(0) {
}
virtual int32_t Decoded(VideoFrame& decoded_image) {
for (int i = 0; i < decoded_image.width(); ++i) {
EXPECT_NEAR(kColorY, decoded_image.buffer(kYPlane)[i], 1);
}
// TODO(mikhal): Verify the difference between U,V and the original.
for (int i = 0; i < ((decoded_image.width() + 1) / 2); ++i) {
EXPECT_NEAR(kColorU, decoded_image.buffer(kUPlane)[i], 4);
EXPECT_NEAR(kColorV, decoded_image.buffer(kVPlane)[i], 4);
}
decoded_frames_++;
return 0;
}
int DecodedFrames() {
return decoded_frames_;
}
private:
int decoded_frames_;
};
class SkipEncodingUnusedStreamsTest {
public:
std::vector<unsigned int> RunTest(VP8Encoder* encoder,
VideoCodec* settings,
uint32_t target_bitrate) {
Config options;
SpyingTemporalLayersFactory* spy_factory =
new SpyingTemporalLayersFactory();
options.Set<TemporalLayers::Factory>(spy_factory);
settings->extra_options = &options;
EXPECT_EQ(0, encoder->InitEncode(settings, 1, 1200));
encoder->SetRates(target_bitrate, 30);
std::vector<unsigned int> configured_bitrates;
for (std::vector<TemporalLayers*>::const_iterator it =
spy_factory->spying_layers_.begin();
it != spy_factory->spying_layers_.end();
++it) {
configured_bitrates.push_back(
static_cast<SpyingTemporalLayers*>(*it)->configured_bitrate_);
}
return configured_bitrates;
}
class SpyingTemporalLayers : public TemporalLayers {
public:
explicit SpyingTemporalLayers(TemporalLayers* layers)
: configured_bitrate_(0), layers_(layers) {}
virtual ~SpyingTemporalLayers() { delete layers_; }
virtual int EncodeFlags(uint32_t timestamp) {
return layers_->EncodeFlags(timestamp);
}
bool ConfigureBitrates(int bitrate_kbit,
int max_bitrate_kbit,
int framerate,
vpx_codec_enc_cfg_t* cfg) override {
configured_bitrate_ = bitrate_kbit;
return layers_->ConfigureBitrates(
bitrate_kbit, max_bitrate_kbit, framerate, cfg);
}
void PopulateCodecSpecific(bool base_layer_sync,
CodecSpecificInfoVP8* vp8_info,
uint32_t timestamp) override {
layers_->PopulateCodecSpecific(base_layer_sync, vp8_info, timestamp);
}
void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) override {
layers_->FrameEncoded(size, timestamp, qp);
}
int CurrentLayerId() const override { return layers_->CurrentLayerId(); }
bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override {
return false;
}
int configured_bitrate_;
TemporalLayers* layers_;
};
class SpyingTemporalLayersFactory : public TemporalLayers::Factory {
public:
virtual ~SpyingTemporalLayersFactory() {}
TemporalLayers* Create(int temporal_layers,
uint8_t initial_tl0_pic_idx) const override {
SpyingTemporalLayers* layers =
new SpyingTemporalLayers(TemporalLayers::Factory::Create(
temporal_layers, initial_tl0_pic_idx));
spying_layers_.push_back(layers);
return layers;
}
mutable std::vector<TemporalLayers*> spying_layers_;
};
};
class TestVp8Simulcast : public ::testing::Test {
public:
TestVp8Simulcast(VP8Encoder* encoder, VP8Decoder* decoder)
: encoder_(encoder),
decoder_(decoder) {}
// Creates an VideoFrame from |plane_colors|.
static void CreateImage(VideoFrame* frame, int plane_colors[kNumOfPlanes]) {
for (int plane_num = 0; plane_num < kNumOfPlanes; ++plane_num) {
int width = (plane_num != kYPlane ? (frame->width() + 1) / 2 :
frame->width());
int height = (plane_num != kYPlane ? (frame->height() + 1) / 2 :
frame->height());
PlaneType plane_type = static_cast<PlaneType>(plane_num);
uint8_t* data = frame->buffer(plane_type);
// Setting allocated area to zero - setting only image size to
// requested values - will make it easier to distinguish between image
// size and frame size (accounting for stride).
memset(frame->buffer(plane_type), 0, frame->allocated_size(plane_type));
for (int i = 0; i < height; i++) {
memset(data, plane_colors[plane_num], width);
data += frame->stride(plane_type);
}
}
}
static void DefaultSettings(VideoCodec* settings,
const int* temporal_layer_profile) {
assert(settings);
memset(settings, 0, sizeof(VideoCodec));
strncpy(settings->plName, "VP8", 4);
settings->codecType = kVideoCodecVP8;
// 96 to 127 dynamic payload types for video codecs
settings->plType = 120;
settings->startBitrate = 300;
settings->minBitrate = 30;
settings->maxBitrate = 0;
settings->maxFramerate = 30;
settings->width = kDefaultWidth;
settings->height = kDefaultHeight;
settings->numberOfSimulcastStreams = kNumberOfSimulcastStreams;
ASSERT_EQ(3, kNumberOfSimulcastStreams);
ConfigureStream(kDefaultWidth / 4, kDefaultHeight / 4,
kMaxBitrates[0],
kMinBitrates[0],
kTargetBitrates[0],
&settings->simulcastStream[0],
temporal_layer_profile[0]);
ConfigureStream(kDefaultWidth / 2, kDefaultHeight / 2,
kMaxBitrates[1],
kMinBitrates[1],
kTargetBitrates[1],
&settings->simulcastStream[1],
temporal_layer_profile[1]);
ConfigureStream(kDefaultWidth, kDefaultHeight,
kMaxBitrates[2],
kMinBitrates[2],
kTargetBitrates[2],
&settings->simulcastStream[2],
temporal_layer_profile[2]);
settings->codecSpecific.VP8.resilience = kResilientStream;
settings->codecSpecific.VP8.denoisingOn = true;
settings->codecSpecific.VP8.errorConcealmentOn = false;
settings->codecSpecific.VP8.automaticResizeOn = false;
settings->codecSpecific.VP8.feedbackModeOn = false;
settings->codecSpecific.VP8.frameDroppingOn = true;
settings->codecSpecific.VP8.keyFrameInterval = 3000;
}
static void ConfigureStream(int width,
int height,
int max_bitrate,
int min_bitrate,
int target_bitrate,
SimulcastStream* stream,
int num_temporal_layers) {
assert(stream);
stream->width = width;
stream->height = height;
stream->maxBitrate = max_bitrate;
stream->minBitrate = min_bitrate;
stream->targetBitrate = target_bitrate;
stream->numberOfTemporalLayers = num_temporal_layers;
stream->qpMax = 45;
}
protected:
virtual void SetUp() {
SetUpCodec(kDefaultTemporalLayerProfile);
}
virtual void SetUpCodec(const int* temporal_layer_profile) {
encoder_->RegisterEncodeCompleteCallback(&encoder_callback_);
decoder_->RegisterDecodeCompleteCallback(&decoder_callback_);
DefaultSettings(&settings_, temporal_layer_profile);
EXPECT_EQ(0, encoder_->InitEncode(&settings_, 1, 1200));
EXPECT_EQ(0, decoder_->InitDecode(&settings_, 1));
int half_width = (kDefaultWidth + 1) / 2;
input_frame_.CreateEmptyFrame(kDefaultWidth, kDefaultHeight,
kDefaultWidth, half_width, half_width);
memset(input_frame_.buffer(kYPlane), 0,
input_frame_.allocated_size(kYPlane));
memset(input_frame_.buffer(kUPlane), 0,
input_frame_.allocated_size(kUPlane));
memset(input_frame_.buffer(kVPlane), 0,
input_frame_.allocated_size(kVPlane));
}
virtual void TearDown() {
encoder_->Release();
decoder_->Release();
}
void ExpectStreams(VideoFrameType frame_type, int expected_video_streams) {
ASSERT_GE(expected_video_streams, 0);
ASSERT_LE(expected_video_streams, kNumberOfSimulcastStreams);
if (expected_video_streams >= 1) {
EXPECT_CALL(encoder_callback_, Encoded(
AllOf(Field(&EncodedImage::_frameType, frame_type),
Field(&EncodedImage::_encodedWidth, kDefaultWidth / 4),
Field(&EncodedImage::_encodedHeight, kDefaultHeight / 4)), _, _)
)
.Times(1)
.WillRepeatedly(Return(0));
}
if (expected_video_streams >= 2) {
EXPECT_CALL(encoder_callback_, Encoded(
AllOf(Field(&EncodedImage::_frameType, frame_type),
Field(&EncodedImage::_encodedWidth, kDefaultWidth / 2),
Field(&EncodedImage::_encodedHeight, kDefaultHeight / 2)), _, _)
)
.Times(1)
.WillRepeatedly(Return(0));
}
if (expected_video_streams >= 3) {
EXPECT_CALL(encoder_callback_, Encoded(
AllOf(Field(&EncodedImage::_frameType, frame_type),
Field(&EncodedImage::_encodedWidth, kDefaultWidth),
Field(&EncodedImage::_encodedHeight, kDefaultHeight)), _, _))
.Times(1)
.WillRepeatedly(Return(0));
}
if (expected_video_streams < kNumberOfSimulcastStreams) {
EXPECT_CALL(encoder_callback_, Encoded(
AllOf(Field(&EncodedImage::_frameType, kSkipFrame),
Field(&EncodedImage::_length, 0)), _, _))
.Times(kNumberOfSimulcastStreams - expected_video_streams)
.WillRepeatedly(Return(0));
}
}
void VerifyTemporalIdxAndSyncForAllSpatialLayers(
Vp8TestEncodedImageCallback* encoder_callback,
const int* expected_temporal_idx,
const bool* expected_layer_sync,
int num_spatial_layers) {
int picture_id = -1;
int temporal_layer = -1;
bool layer_sync = false;
for (int i = 0; i < num_spatial_layers; i++) {
encoder_callback->GetLastEncodedFrameInfo(&picture_id, &temporal_layer,
&layer_sync, i);
EXPECT_EQ(expected_temporal_idx[i], temporal_layer);
EXPECT_EQ(expected_layer_sync[i], layer_sync);
}
}
// We currently expect all active streams to generate a key frame even though
// a key frame was only requested for some of them.
void TestKeyFrameRequestsOnAllStreams() {
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
std::vector<VideoFrameType> frame_types(kNumberOfSimulcastStreams,
kDeltaFrame);
ExpectStreams(kKeyFrame, kNumberOfSimulcastStreams);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
ExpectStreams(kDeltaFrame, kNumberOfSimulcastStreams);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
frame_types[0] = kKeyFrame;
ExpectStreams(kKeyFrame, kNumberOfSimulcastStreams);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
std::fill(frame_types.begin(), frame_types.end(), kDeltaFrame);
frame_types[1] = kKeyFrame;
ExpectStreams(kKeyFrame, kNumberOfSimulcastStreams);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
std::fill(frame_types.begin(), frame_types.end(), kDeltaFrame);
frame_types[2] = kKeyFrame;
ExpectStreams(kKeyFrame, kNumberOfSimulcastStreams);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
std::fill(frame_types.begin(), frame_types.end(), kDeltaFrame);
ExpectStreams(kDeltaFrame, kNumberOfSimulcastStreams);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
}
void TestPaddingAllStreams() {
// We should always encode the base layer.
encoder_->SetRates(kMinBitrates[0] - 1, 30);
std::vector<VideoFrameType> frame_types(kNumberOfSimulcastStreams,
kDeltaFrame);
ExpectStreams(kKeyFrame, 1);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
ExpectStreams(kDeltaFrame, 1);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
}
void TestPaddingTwoStreams() {
// We have just enough to get only the first stream and padding for two.
encoder_->SetRates(kMinBitrates[0], 30);
std::vector<VideoFrameType> frame_types(kNumberOfSimulcastStreams,
kDeltaFrame);
ExpectStreams(kKeyFrame, 1);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
ExpectStreams(kDeltaFrame, 1);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
}
void TestPaddingTwoStreamsOneMaxedOut() {
// We are just below limit of sending second stream, so we should get
// the first stream maxed out (at |maxBitrate|), and padding for two.
encoder_->SetRates(kTargetBitrates[0] + kMinBitrates[1] - 1, 30);
std::vector<VideoFrameType> frame_types(kNumberOfSimulcastStreams,
kDeltaFrame);
ExpectStreams(kKeyFrame, 1);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
ExpectStreams(kDeltaFrame, 1);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
}
void TestPaddingOneStream() {
// We have just enough to send two streams, so padding for one stream.
encoder_->SetRates(kTargetBitrates[0] + kMinBitrates[1], 30);
std::vector<VideoFrameType> frame_types(kNumberOfSimulcastStreams,
kDeltaFrame);
ExpectStreams(kKeyFrame, 2);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
ExpectStreams(kDeltaFrame, 2);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
}
void TestPaddingOneStreamTwoMaxedOut() {
// We are just below limit of sending third stream, so we should get
// first stream's rate maxed out at |targetBitrate|, second at |maxBitrate|.
encoder_->SetRates(kTargetBitrates[0] + kTargetBitrates[1] +
kMinBitrates[2] - 1, 30);
std::vector<VideoFrameType> frame_types(kNumberOfSimulcastStreams,
kDeltaFrame);
ExpectStreams(kKeyFrame, 2);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
ExpectStreams(kDeltaFrame, 2);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
}
void TestSendAllStreams() {
// We have just enough to send all streams.
encoder_->SetRates(kTargetBitrates[0] + kTargetBitrates[1] +
kMinBitrates[2], 30);
std::vector<VideoFrameType> frame_types(kNumberOfSimulcastStreams,
kDeltaFrame);
ExpectStreams(kKeyFrame, 3);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
ExpectStreams(kDeltaFrame, 3);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
}
void TestDisablingStreams() {
// We should get three media streams.
encoder_->SetRates(kMaxBitrates[0] + kMaxBitrates[1] +
kMaxBitrates[2], 30);
std::vector<VideoFrameType> frame_types(kNumberOfSimulcastStreams,
kDeltaFrame);
ExpectStreams(kKeyFrame, 3);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
ExpectStreams(kDeltaFrame, 3);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
// We should only get two streams and padding for one.
encoder_->SetRates(kTargetBitrates[0] + kTargetBitrates[1] +
kMinBitrates[2] / 2, 30);
ExpectStreams(kDeltaFrame, 2);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
// We should only get the first stream and padding for two.
encoder_->SetRates(kTargetBitrates[0] + kMinBitrates[1] / 2, 30);
ExpectStreams(kDeltaFrame, 1);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
// We don't have enough bitrate for the thumbnail stream, but we should get
// it anyway with current configuration.
encoder_->SetRates(kTargetBitrates[0] - 1, 30);
ExpectStreams(kDeltaFrame, 1);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
// We should only get two streams and padding for one.
encoder_->SetRates(kTargetBitrates[0] + kTargetBitrates[1] +
kMinBitrates[2] / 2, 30);
// We get a key frame because a new stream is being enabled.
ExpectStreams(kKeyFrame, 2);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
// We should get all three streams.
encoder_->SetRates(kTargetBitrates[0] + kTargetBitrates[1] +
kTargetBitrates[2], 30);
// We get a key frame because a new stream is being enabled.
ExpectStreams(kKeyFrame, 3);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
}
void SwitchingToOneStream(int width, int height) {
// Disable all streams except the last and set the bitrate of the last to
// 100 kbps. This verifies the way GTP switches to screenshare mode.
settings_.codecSpecific.VP8.numberOfTemporalLayers = 1;
settings_.maxBitrate = 100;
settings_.startBitrate = 100;
settings_.width = width;
settings_.height = height;
for (int i = 0; i < settings_.numberOfSimulcastStreams - 1; ++i) {
settings_.simulcastStream[i].maxBitrate = 0;
settings_.simulcastStream[i].width = settings_.width;
settings_.simulcastStream[i].height = settings_.height;
}
// Setting input image to new resolution.
int half_width = (settings_.width + 1) / 2;
input_frame_.CreateEmptyFrame(settings_.width, settings_.height,
settings_.width, half_width, half_width);
memset(input_frame_.buffer(kYPlane), 0,
input_frame_.allocated_size(kYPlane));
memset(input_frame_.buffer(kUPlane), 0,
input_frame_.allocated_size(kUPlane));
memset(input_frame_.buffer(kVPlane), 0,
input_frame_.allocated_size(kVPlane));
// The for loop above did not set the bitrate of the highest layer.
settings_.simulcastStream[settings_.numberOfSimulcastStreams - 1].
maxBitrate = 0;
// The highest layer has to correspond to the non-simulcast resolution.
settings_.simulcastStream[settings_.numberOfSimulcastStreams - 1].
width = settings_.width;
settings_.simulcastStream[settings_.numberOfSimulcastStreams - 1].
height = settings_.height;
EXPECT_EQ(0, encoder_->InitEncode(&settings_, 1, 1200));
// Encode one frame and verify.
encoder_->SetRates(kMaxBitrates[0] + kMaxBitrates[1], 30);
std::vector<VideoFrameType> frame_types(kNumberOfSimulcastStreams,
kDeltaFrame);
EXPECT_CALL(encoder_callback_, Encoded(
AllOf(Field(&EncodedImage::_frameType, kKeyFrame),
Field(&EncodedImage::_encodedWidth, width),
Field(&EncodedImage::_encodedHeight, height)), _, _))
.Times(1)
.WillRepeatedly(Return(0));
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
// Switch back.
DefaultSettings(&settings_, kDefaultTemporalLayerProfile);
// Start at the lowest bitrate for enabling base stream.
settings_.startBitrate = kMinBitrates[0];
EXPECT_EQ(0, encoder_->InitEncode(&settings_, 1, 1200));
encoder_->SetRates(settings_.startBitrate, 30);
ExpectStreams(kKeyFrame, 1);
// Resize |input_frame_| to the new resolution.
half_width = (settings_.width + 1) / 2;
input_frame_.CreateEmptyFrame(settings_.width, settings_.height,
settings_.width, half_width, half_width);
memset(input_frame_.buffer(kYPlane), 0,
input_frame_.allocated_size(kYPlane));
memset(input_frame_.buffer(kUPlane), 0,
input_frame_.allocated_size(kUPlane));
memset(input_frame_.buffer(kVPlane), 0,
input_frame_.allocated_size(kVPlane));
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, &frame_types));
}
void TestSwitchingToOneStream() {
SwitchingToOneStream(1024, 768);
}
void TestSwitchingToOneOddStream() {
SwitchingToOneStream(1023, 769);
}
void TestRPSIEncoder() {
Vp8TestEncodedImageCallback encoder_callback;
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
int picture_id = -1;
int temporal_layer = -1;
bool layer_sync = false;
encoder_callback.GetLastEncodedFrameInfo(&picture_id, &temporal_layer,
&layer_sync, 0);
EXPECT_EQ(0, temporal_layer);
EXPECT_TRUE(layer_sync);
int key_frame_picture_id = picture_id;
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
encoder_callback.GetLastEncodedFrameInfo(&picture_id, &temporal_layer,
&layer_sync, 0);
EXPECT_EQ(2, temporal_layer);
EXPECT_TRUE(layer_sync);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
encoder_callback.GetLastEncodedFrameInfo(&picture_id, &temporal_layer,
&layer_sync, 0);
EXPECT_EQ(1, temporal_layer);
EXPECT_TRUE(layer_sync);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
encoder_callback.GetLastEncodedFrameInfo(&picture_id, &temporal_layer,
&layer_sync, 0);
EXPECT_EQ(2, temporal_layer);
EXPECT_FALSE(layer_sync);
CodecSpecificInfo codec_specific;
codec_specific.codecType = kVideoCodecVP8;
codec_specific.codecSpecific.VP8.hasReceivedRPSI = true;
// Must match last key frame to trigger.
codec_specific.codecSpecific.VP8.pictureIdRPSI = key_frame_picture_id;
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, &codec_specific, NULL));
encoder_callback.GetLastEncodedFrameInfo(&picture_id, &temporal_layer,
&layer_sync, 0);
EXPECT_EQ(0, temporal_layer);
EXPECT_TRUE(layer_sync);
// Must match last key frame to trigger, test bad id.
codec_specific.codecSpecific.VP8.pictureIdRPSI = key_frame_picture_id + 17;
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, &codec_specific, NULL));
encoder_callback.GetLastEncodedFrameInfo(&picture_id, &temporal_layer,
&layer_sync, 0);
EXPECT_EQ(2, temporal_layer);
// The previous frame was a base layer sync (since it was a frame that
// only predicts from key frame and hence resets the temporal pattern),
// so this frame (the next one) must have |layer_sync| set to true.
EXPECT_TRUE(layer_sync);
}
void TestRPSIEncodeDecode() {
Vp8TestEncodedImageCallback encoder_callback;
Vp8TestDecodedImageCallback decoder_callback;
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
decoder_->RegisterDecodeCompleteCallback(&decoder_callback);
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
// Set color.
int plane_offset[kNumOfPlanes];
plane_offset[kYPlane] = kColorY;
plane_offset[kUPlane] = kColorU;
plane_offset[kVPlane] = kColorV;
CreateImage(&input_frame_, plane_offset);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
int picture_id = -1;
int temporal_layer = -1;
bool layer_sync = false;
encoder_callback.GetLastEncodedFrameInfo(&picture_id, &temporal_layer,
&layer_sync, 0);
EXPECT_EQ(0, temporal_layer);
EXPECT_TRUE(layer_sync);
int key_frame_picture_id = picture_id;
// Change color.
plane_offset[kYPlane] += 1;
plane_offset[kUPlane] += 1;
plane_offset[kVPlane] += 1;
CreateImage(&input_frame_, plane_offset);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
// Change color.
plane_offset[kYPlane] += 1;
plane_offset[kUPlane] += 1;
plane_offset[kVPlane] += 1;
CreateImage(&input_frame_, plane_offset);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
// Change color.
plane_offset[kYPlane] += 1;
plane_offset[kUPlane] += 1;
plane_offset[kVPlane] += 1;
CreateImage(&input_frame_, plane_offset);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
CodecSpecificInfo codec_specific;
codec_specific.codecType = kVideoCodecVP8;
codec_specific.codecSpecific.VP8.hasReceivedRPSI = true;
// Must match last key frame to trigger.
codec_specific.codecSpecific.VP8.pictureIdRPSI = key_frame_picture_id;
// Change color back to original.
plane_offset[kYPlane] = kColorY;
plane_offset[kUPlane] = kColorU;
plane_offset[kVPlane] = kColorV;
CreateImage(&input_frame_, plane_offset);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, &codec_specific, NULL));
EncodedImage encoded_frame;
encoder_callback.GetLastEncodedKeyFrame(&encoded_frame);
decoder_->Decode(encoded_frame, false, NULL);
encoder_callback.GetLastEncodedFrame(&encoded_frame);
decoder_->Decode(encoded_frame, false, NULL);
EXPECT_EQ(2, decoder_callback.DecodedFrames());
}
// Test the layer pattern and sync flag for various spatial-temporal patterns.
// 3-3-3 pattern: 3 temporal layers for all spatial streams, so same
// temporal_layer id and layer_sync is expected for all streams.
void TestSaptioTemporalLayers333PatternEncoder() {
Vp8TestEncodedImageCallback encoder_callback;
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
int expected_temporal_idx[3] = { -1, -1, -1};
bool expected_layer_sync[3] = {false, false, false};
// First frame: #0.
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
SetExpectedValues3<int>(0, 0, 0, expected_temporal_idx);
SetExpectedValues3<bool>(true, true, true, expected_layer_sync);
VerifyTemporalIdxAndSyncForAllSpatialLayers(&encoder_callback,
expected_temporal_idx,
expected_layer_sync,
3);
// Next frame: #1.
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
SetExpectedValues3<int>(2, 2, 2, expected_temporal_idx);
SetExpectedValues3<bool>(true, true, true, expected_layer_sync);
VerifyTemporalIdxAndSyncForAllSpatialLayers(&encoder_callback,
expected_temporal_idx,
expected_layer_sync,
3);
// Next frame: #2.
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
SetExpectedValues3<int>(1, 1, 1, expected_temporal_idx);
SetExpectedValues3<bool>(true, true, true, expected_layer_sync);
VerifyTemporalIdxAndSyncForAllSpatialLayers(&encoder_callback,
expected_temporal_idx,
expected_layer_sync,
3);
// Next frame: #3.
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
SetExpectedValues3<int>(2, 2, 2, expected_temporal_idx);
SetExpectedValues3<bool>(false, false, false, expected_layer_sync);
VerifyTemporalIdxAndSyncForAllSpatialLayers(&encoder_callback,
expected_temporal_idx,
expected_layer_sync,
3);
// Next frame: #4.
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
SetExpectedValues3<int>(0, 0, 0, expected_temporal_idx);
SetExpectedValues3<bool>(false, false, false, expected_layer_sync);
VerifyTemporalIdxAndSyncForAllSpatialLayers(&encoder_callback,
expected_temporal_idx,
expected_layer_sync,
3);
// Next frame: #5.
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
SetExpectedValues3<int>(2, 2, 2, expected_temporal_idx);
SetExpectedValues3<bool>(false, false, false, expected_layer_sync);
VerifyTemporalIdxAndSyncForAllSpatialLayers(&encoder_callback,
expected_temporal_idx,
expected_layer_sync,
3);
}
// Test the layer pattern and sync flag for various spatial-temporal patterns.
// 3-2-1 pattern: 3 temporal layers for lowest resolution, 2 for middle, and
// 1 temporal layer for highest resolution.
// For this profile, we expect the temporal index pattern to be:
// 1st stream: 0, 2, 1, 2, ....
// 2nd stream: 0, 1, 0, 1, ...
// 3rd stream: -1, -1, -1, -1, ....
// Regarding the 3rd stream, note that a stream/encoder with 1 temporal layer
// should always have temporal layer idx set to kNoTemporalIdx = -1.
// Since CodecSpecificInfoVP8.temporalIdx is uint8, this will wrap to 255.
// TODO(marpan): Although this seems safe for now, we should fix this.
void TestSpatioTemporalLayers321PatternEncoder() {
int temporal_layer_profile[3] = {3, 2, 1};
SetUpCodec(temporal_layer_profile);
Vp8TestEncodedImageCallback encoder_callback;
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
int expected_temporal_idx[3] = { -1, -1, -1};
bool expected_layer_sync[3] = {false, false, false};
// First frame: #0.
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
SetExpectedValues3<int>(0, 0, 255, expected_temporal_idx);
SetExpectedValues3<bool>(true, true, false, expected_layer_sync);
VerifyTemporalIdxAndSyncForAllSpatialLayers(&encoder_callback,
expected_temporal_idx,
expected_layer_sync,
3);
// Next frame: #1.
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
SetExpectedValues3<int>(2, 1, 255, expected_temporal_idx);
SetExpectedValues3<bool>(true, true, false, expected_layer_sync);
VerifyTemporalIdxAndSyncForAllSpatialLayers(&encoder_callback,
expected_temporal_idx,
expected_layer_sync,
3);
// Next frame: #2.
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
SetExpectedValues3<int>(1, 0, 255, expected_temporal_idx);
SetExpectedValues3<bool>(true, false, false, expected_layer_sync);
VerifyTemporalIdxAndSyncForAllSpatialLayers(&encoder_callback,
expected_temporal_idx,
expected_layer_sync,
3);
// Next frame: #3.
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
SetExpectedValues3<int>(2, 1, 255, expected_temporal_idx);
SetExpectedValues3<bool>(false, false, false, expected_layer_sync);
VerifyTemporalIdxAndSyncForAllSpatialLayers(&encoder_callback,
expected_temporal_idx,
expected_layer_sync,
3);
// Next frame: #4.
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
SetExpectedValues3<int>(0, 0, 255, expected_temporal_idx);
SetExpectedValues3<bool>(false, false, false, expected_layer_sync);
VerifyTemporalIdxAndSyncForAllSpatialLayers(&encoder_callback,
expected_temporal_idx,
expected_layer_sync,
3);
// Next frame: #5.
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
SetExpectedValues3<int>(2, 1, 255, expected_temporal_idx);
SetExpectedValues3<bool>(false, false, false, expected_layer_sync);
VerifyTemporalIdxAndSyncForAllSpatialLayers(&encoder_callback,
expected_temporal_idx,
expected_layer_sync,
3);
}
void TestStrideEncodeDecode() {
Vp8TestEncodedImageCallback encoder_callback;
Vp8TestDecodedImageCallback decoder_callback;
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
decoder_->RegisterDecodeCompleteCallback(&decoder_callback);
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
// Setting two (possibly) problematic use cases for stride:
// 1. stride > width 2. stride_y != stride_uv/2
int stride_y = kDefaultWidth + 20;
int stride_uv = ((kDefaultWidth + 1) / 2) + 5;
input_frame_.CreateEmptyFrame(kDefaultWidth, kDefaultHeight,
stride_y, stride_uv, stride_uv);
// Set color.
int plane_offset[kNumOfPlanes];
plane_offset[kYPlane] = kColorY;
plane_offset[kUPlane] = kColorU;
plane_offset[kVPlane] = kColorV;
CreateImage(&input_frame_, plane_offset);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
// Change color.
plane_offset[kYPlane] += 1;
plane_offset[kUPlane] += 1;
plane_offset[kVPlane] += 1;
CreateImage(&input_frame_, plane_offset);
input_frame_.set_timestamp(input_frame_.timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(input_frame_, NULL, NULL));
EncodedImage encoded_frame;
// Only encoding one frame - so will be a key frame.
encoder_callback.GetLastEncodedKeyFrame(&encoded_frame);
EXPECT_EQ(0, decoder_->Decode(encoded_frame, false, NULL));
encoder_callback.GetLastEncodedFrame(&encoded_frame);
decoder_->Decode(encoded_frame, false, NULL);
EXPECT_EQ(2, decoder_callback.DecodedFrames());
}
void TestSkipEncodingUnusedStreams() {
SkipEncodingUnusedStreamsTest test;
std::vector<unsigned int> configured_bitrate =
test.RunTest(encoder_.get(),
&settings_,
1); // Target bit rate 1, to force all streams but the
// base one to be exceeding bandwidth constraints.
EXPECT_EQ(static_cast<size_t>(kNumberOfSimulcastStreams),
configured_bitrate.size());
unsigned int min_bitrate =
std::max(settings_.simulcastStream[0].minBitrate, settings_.minBitrate);
int stream = 0;
for (std::vector<unsigned int>::const_iterator it =
configured_bitrate.begin();
it != configured_bitrate.end();
++it) {
if (stream == 0) {
EXPECT_EQ(min_bitrate, *it);
} else {
EXPECT_EQ(0u, *it);
}
++stream;
}
}
rtc::scoped_ptr<VP8Encoder> encoder_;
MockEncodedImageCallback encoder_callback_;
rtc::scoped_ptr<VP8Decoder> decoder_;
MockDecodedImageCallback decoder_callback_;
VideoCodec settings_;
VideoFrame input_frame_;
};
} // namespace testing
} // namespace webrtc
#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_SIMULCAST_UNITTEST_H_