Adds two tests for verifying padding and ramp-up behavior.

BUG=1837
R=pbos@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/2073004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@4591 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
stefan@webrtc.org 2013-08-22 09:29:56 +00:00
parent 3365422c41
commit 360e376872
7 changed files with 392 additions and 5 deletions

View File

@ -16,6 +16,7 @@
#include "webrtc/video_engine/include/vie_base.h"
#include "webrtc/video_engine/include/vie_capture.h"
#include "webrtc/video_engine/include/vie_codec.h"
#include "webrtc/video_engine/include/vie_external_codec.h"
#include "webrtc/video_engine/include/vie_network.h"
#include "webrtc/video_engine/include/vie_rtp_rtcp.h"
#include "webrtc/video_engine/new_include/video_send_stream.h"
@ -77,7 +78,7 @@ VideoSendStream::VideoSendStream(newapi::Transport* transport,
bool overuse_detection,
webrtc::VideoEngine* video_engine,
const newapi::VideoSendStream::Config& config)
: transport_(transport), config_(config) {
: transport_(transport), config_(config), external_codec_(NULL) {
if (config_.codec.numberOfSimulcastStreams > 0) {
assert(config_.rtp.ssrcs.size() == config_.codec.numberOfSimulcastStreams);
@ -92,9 +93,17 @@ VideoSendStream::VideoSendStream(newapi::Transport* transport,
rtp_rtcp_ = ViERTP_RTCP::GetInterface(video_engine);
assert(rtp_rtcp_ != NULL);
assert(config_.rtp.ssrcs.size() == 1);
rtp_rtcp_->SetLocalSSRC(channel_, config_.rtp.ssrcs[0]);
if (config_.rtp.ssrcs.size() == 1) {
rtp_rtcp_->SetLocalSSRC(channel_, config_.rtp.ssrcs[0]);
} else {
for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) {
rtp_rtcp_->SetLocalSSRC(channel_, config_.rtp.ssrcs[i],
kViEStreamTypeNormal, i);
}
}
rtp_rtcp_->SetNACKStatus(channel_, config_.rtp.nack.rtp_history_ms > 0);
rtp_rtcp_->SetTransmissionSmoothingStatus(channel_, config_.pacing);
rtp_rtcp_->SetSendTimestampOffsetStatus(channel_, true, 1);
capture_ = ViECapture::GetInterface(video_engine);
capture_->AllocateExternalCaptureDevice(capture_id_, external_capture_);
@ -105,6 +114,15 @@ VideoSendStream::VideoSendStream(newapi::Transport* transport,
network_->RegisterSendTransport(channel_, *this);
if (config.encoder) {
external_codec_ = ViEExternalCodec::GetInterface(video_engine);
if (external_codec_->RegisterExternalSendCodec(
channel_, config.codec.plType, config.encoder,
config.internal_source) != 0) {
abort();
}
}
codec_ = ViECodec::GetInterface(video_engine);
if (codec_->SetSendCodec(channel_, config_.codec) != 0) {
abort();
@ -126,9 +144,16 @@ VideoSendStream::~VideoSendStream() {
capture_->DisconnectCaptureDevice(channel_);
capture_->ReleaseCaptureDevice(capture_id_);
if (external_codec_) {
external_codec_->DeRegisterExternalSendCodec(channel_,
config_.codec.plType);
}
video_engine_base_->Release();
capture_->Release();
codec_->Release();
if (external_codec_)
external_codec_->Release();
network_->Release();
rtp_rtcp_->Release();
}
@ -199,7 +224,7 @@ int VideoSendStream::SendPacket(int /*channel*/,
assert(length >= 0);
bool success = transport_->SendRTP(static_cast<const uint8_t*>(packet),
static_cast<size_t>(length));
return success ? 0 : -1;
return success ? length : -1;
}
int VideoSendStream::SendRTCPPacket(int /*channel*/,
@ -208,7 +233,7 @@ int VideoSendStream::SendRTCPPacket(int /*channel*/,
assert(length >= 0);
bool success = transport_->SendRTCP(static_cast<const uint8_t*>(packet),
static_cast<size_t>(length));
return success ? 0 : -1;
return success ? length : -1;
}
bool VideoSendStream::DeliverRtcp(const uint8_t* packet, size_t length) {

View File

@ -24,6 +24,7 @@ class ViEBase;
class ViECapture;
class ViECodec;
class ViEExternalCapture;
class ViEExternalCodec;
class ViENetwork;
class ViERTP_RTCP;
@ -74,6 +75,7 @@ class VideoSendStream : public newapi::VideoSendStream,
ViECapture* capture_;
ViECodec* codec_;
ViEExternalCapture* external_capture_;
ViEExternalCodec* external_codec_;
ViENetwork* network_;
ViERTP_RTCP* rtp_rtcp_;

View File

@ -81,6 +81,7 @@ class VideoSendStream {
encoder(NULL),
internal_source(false),
target_delay_ms(0),
pacing(false),
stats_callback(NULL),
start_state(NULL) {}
VideoCodec codec;
@ -138,6 +139,10 @@ class VideoSendStream {
// used for streaming instead of a real-time call.
int target_delay_ms;
// True if network a send-side packet buffer should be used to pace out
// packets onto the network.
bool pacing;
// Callback for periodically receiving send stats.
StatsCallback* stats_callback;

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2013 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.
*/
#include "webrtc/video_engine/test/common/fake_encoder.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace webrtc {
FakeEncoder::FakeEncoder(Clock* clock)
: clock_(clock),
callback_(NULL),
target_bitrate_kbps_(0),
last_encode_time_ms_(0) {
memset(encoded_buffer_, 0, sizeof(encoded_buffer_));
}
FakeEncoder::~FakeEncoder() {}
int32_t FakeEncoder::InitEncode(const VideoCodec* config,
int32_t number_of_cores,
uint32_t max_payload_size) {
config_ = *config;
target_bitrate_kbps_ = config_.startBitrate;
return 0;
}
int32_t FakeEncoder::Encode(
const I420VideoFrame& input_image,
const CodecSpecificInfo* codec_specific_info,
const std::vector<VideoFrameType>* frame_types) {
assert(config_.maxFramerate > 0);
int delta_since_last_encode = 1000 / config_.maxFramerate;
int64_t time_now_ms = clock_->TimeInMilliseconds();
if (last_encode_time_ms_ > 0) {
// For all frames but the first we can estimate the display time by looking
// at the display time of the previous frame.
delta_since_last_encode = time_now_ms - last_encode_time_ms_;
}
int bits_available = target_bitrate_kbps_ * delta_since_last_encode;
last_encode_time_ms_ = time_now_ms;
for (int i = 0; i < config_.numberOfSimulcastStreams; ++i) {
CodecSpecificInfo specifics;
memset(&specifics, 0, sizeof(specifics));
specifics.codecType = kVideoCodecVP8;
specifics.codecSpecific.VP8.simulcastIdx = i;
int min_stream_bits = config_.simulcastStream[i].minBitrate *
delta_since_last_encode;
int max_stream_bits = config_.simulcastStream[i].maxBitrate *
delta_since_last_encode;
int stream_bits = (bits_available > max_stream_bits) ? max_stream_bits :
bits_available;
int stream_bytes = (stream_bits + 7) / 8;
EXPECT_LT(stream_bytes, kMaxFrameSizeBytes);
if (stream_bytes > kMaxFrameSizeBytes)
return -1;
EncodedImage encoded(encoded_buffer_, stream_bytes, kMaxFrameSizeBytes);
encoded._timeStamp = input_image.timestamp();
encoded.capture_time_ms_ = input_image.render_time_ms();
if (min_stream_bits > bits_available) {
encoded._length = 0;
encoded._frameType = kSkipFrame;
}
if (callback_->Encoded(encoded, &specifics, NULL) != 0)
return -1;
bits_available -= encoded._length * 8;
}
return 0;
}
int32_t FakeEncoder::RegisterEncodeCompleteCallback(
EncodedImageCallback* callback) {
callback_ = callback;
return 0;
}
int32_t FakeEncoder::Release() { return 0; }
int32_t FakeEncoder::SetChannelParameters(uint32_t packet_loss, int rtt) {
return 0;
}
int32_t FakeEncoder::SetRates(uint32_t new_target_bitrate, uint32_t framerate) {
target_bitrate_kbps_ = new_target_bitrate;
return 0;
}
} // namespace webrtc

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2013 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_VIDEO_ENGINE_TEST_COMMON_FAKE_ENCODER_H_
#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_FAKE_ENCODER_H_
#include <vector>
#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
#include "webrtc/system_wrappers/interface/clock.h"
namespace webrtc {
class FakeEncoder : public VideoEncoder {
public:
explicit FakeEncoder(Clock* clock);
virtual ~FakeEncoder();
virtual int32_t InitEncode(const VideoCodec* config,
int32_t number_of_cores,
uint32_t max_payload_size) OVERRIDE;
virtual int32_t Encode(
const I420VideoFrame& input_image,
const CodecSpecificInfo* codec_specific_info,
const std::vector<VideoFrameType>* frame_types) OVERRIDE;
virtual int32_t RegisterEncodeCompleteCallback(
EncodedImageCallback* callback) OVERRIDE;
virtual int32_t Release() OVERRIDE;
virtual int32_t SetChannelParameters(uint32_t packet_loss, int rtt) OVERRIDE;
virtual int32_t SetRates(uint32_t new_target_bitrate,
uint32_t framerate) OVERRIDE;
private:
enum { kMaxFrameSizeBytes = 100000 };
Clock* clock_;
VideoCodec config_;
EncodedImageCallback* callback_;
int target_bitrate_kbps_;
int64_t last_encode_time_ms_;
uint8_t encoded_buffer_[kMaxFrameSizeBytes];
};
} // namespace webrtc
#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_FAKE_ENCODER_H_

View File

@ -13,13 +13,17 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
#include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/system_wrappers/interface/event_wrapper.h"
#include "webrtc/video_engine/new_include/video_call.h"
#include "webrtc/video_engine/test/common/direct_transport.h"
#include "webrtc/video_engine/test/common/fake_encoder.h"
#include "webrtc/video_engine/test/common/frame_generator.h"
#include "webrtc/video_engine/test/common/frame_generator_capturer.h"
#include "webrtc/video_engine/test/common/generate_ssrcs.h"
@ -27,6 +31,199 @@
namespace webrtc {
class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
public:
typedef std::map<uint32_t, int> BytesSentMap;
StreamObserver(int num_expected_ssrcs, newapi::Transport* feedback_transport,
Clock* clock)
: critical_section_(CriticalSectionWrapper::CreateCriticalSection()),
all_ssrcs_sent_(EventWrapper::Create()),
rtp_parser_(RtpHeaderParser::Create()),
feedback_transport_(new TransportWrapper(feedback_transport)),
receive_stats_(ReceiveStatistics::Create(clock)),
clock_(clock),
num_expected_ssrcs_(num_expected_ssrcs) {
// Ideally we would only have to instantiate an RtcpSender, an
// RtpHeaderParser and a RemoteBitrateEstimator here, but due to the current
// state of the RTP module we need a full module and receive statistics to
// be able to produce an RTCP with REMB.
RtpRtcp::Configuration config;
config.receive_statistics = receive_stats_.get();
config.outgoing_transport = feedback_transport_.get();
rtp_rtcp_.reset(RtpRtcp::CreateRtpRtcp(config));
rtp_rtcp_->SetREMBStatus(true);
rtp_rtcp_->SetRTCPStatus(kRtcpNonCompound);
rtp_parser_->RegisterRtpHeaderExtension(kRtpExtensionTransmissionTimeOffset,
1);
AbsoluteSendTimeRemoteBitrateEstimatorFactory rbe_factory;
remote_bitrate_estimator_.reset(rbe_factory.Create(this, clock));
}
virtual void OnReceiveBitrateChanged(const std::vector<unsigned int>& ssrcs,
unsigned int bitrate) {
CriticalSectionScoped lock(critical_section_.get());
if (ssrcs.size() == num_expected_ssrcs_ && bitrate >= kExpectedBitrateBps)
all_ssrcs_sent_->Set();
rtp_rtcp_->SetREMBData(bitrate, static_cast<uint8_t>(ssrcs.size()),
&ssrcs[0]);
rtp_rtcp_->Process();
}
virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
CriticalSectionScoped lock(critical_section_.get());
RTPHeader header;
EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast<int>(length),
&header));
receive_stats_->IncomingPacket(header, length, false, true);
rtp_rtcp_->SetRemoteSSRC(header.ssrc);
remote_bitrate_estimator_->IncomingPacket(clock_->TimeInMilliseconds(),
static_cast<int>(length - 12),
header);
if (remote_bitrate_estimator_->TimeUntilNextProcess() <= 0) {
remote_bitrate_estimator_->Process();
}
return true;
}
virtual bool SendRTCP(const uint8_t* packet, size_t length) OVERRIDE {
return true;
}
EventTypeWrapper Wait() {
return all_ssrcs_sent_->Wait(120 * 1000);
}
private:
class TransportWrapper : public webrtc::Transport {
public:
explicit TransportWrapper(newapi::Transport* new_transport)
: new_transport_(new_transport) {}
virtual int SendPacket(int channel, const void *data, int len) OVERRIDE {
return new_transport_->SendRTP(static_cast<const uint8_t*>(data), len) ?
len : -1;
}
virtual int SendRTCPPacket(int channel, const void *data,
int len) OVERRIDE {
return new_transport_->SendRTCP(static_cast<const uint8_t*>(data), len) ?
len : -1;
}
private:
newapi::Transport* new_transport_;
};
static const unsigned int kExpectedBitrateBps = 1200000;
scoped_ptr<CriticalSectionWrapper> critical_section_;
scoped_ptr<EventWrapper> all_ssrcs_sent_;
scoped_ptr<RtpHeaderParser> rtp_parser_;
scoped_ptr<RtpRtcp> rtp_rtcp_;
scoped_ptr<TransportWrapper> feedback_transport_;
scoped_ptr<ReceiveStatistics> receive_stats_;
scoped_ptr<RemoteBitrateEstimator> remote_bitrate_estimator_;
Clock* clock_;
const size_t num_expected_ssrcs_;
};
class RampUpTest : public ::testing::TestWithParam<bool> {
public:
virtual void SetUp() {
reserved_ssrcs_.clear();
}
static void SetCodecStreamSettings(VideoCodec* video_codec) {
video_codec->width = 1280;
video_codec->height = 720;
video_codec->startBitrate = 300;
video_codec->minBitrate = 50;
video_codec->maxBitrate = 1800;
video_codec->numberOfSimulcastStreams = 3;
video_codec->simulcastStream[0].width = 320;
video_codec->simulcastStream[0].height = 180;
video_codec->simulcastStream[0].numberOfTemporalLayers = 0;
video_codec->simulcastStream[0].maxBitrate = 150;
video_codec->simulcastStream[0].targetBitrate = 150;
video_codec->simulcastStream[0].minBitrate = 50;
video_codec->simulcastStream[0].qpMax = video_codec->qpMax;
video_codec->simulcastStream[1].width = 640;
video_codec->simulcastStream[1].height = 360;
video_codec->simulcastStream[1].numberOfTemporalLayers = 0;
video_codec->simulcastStream[1].maxBitrate = 500;
video_codec->simulcastStream[1].targetBitrate = 500;
video_codec->simulcastStream[1].minBitrate = 150;
video_codec->simulcastStream[1].qpMax = video_codec->qpMax;
video_codec->simulcastStream[2].width = 1280;
video_codec->simulcastStream[2].height = 720;
video_codec->simulcastStream[2].numberOfTemporalLayers = 0;
video_codec->simulcastStream[2].maxBitrate = 1200;
video_codec->simulcastStream[2].targetBitrate = 1200;
video_codec->simulcastStream[2].minBitrate = 600;
video_codec->simulcastStream[2].qpMax = video_codec->qpMax;
}
protected:
std::map<uint32_t, bool> reserved_ssrcs_;
};
TEST_P(RampUpTest, RampUpWithPadding) {
test::DirectTransport receiver_transport;
StreamObserver stream_observer(3, &receiver_transport,
Clock::GetRealTimeClock());
newapi::VideoCall::Config call_config(&stream_observer);
scoped_ptr<newapi::VideoCall> call(newapi::VideoCall::Create(call_config));
newapi::VideoSendStream::Config send_config =
call->GetDefaultSendConfig();
receiver_transport.SetReceiver(call->Receiver());
FakeEncoder encoder(Clock::GetRealTimeClock());
send_config.encoder = &encoder;
send_config.internal_source = false;
SetCodecStreamSettings(&send_config.codec);
send_config.codec.plType = 100;
send_config.pacing = GetParam();
test::GenerateRandomSsrcs(&send_config, &reserved_ssrcs_);
newapi::VideoSendStream* send_stream =
call->CreateSendStream(send_config);
newapi::VideoReceiveStream::Config receive_config;
receive_config.rtp.ssrc = send_config.rtp.ssrcs[0];
receive_config.rtp.nack.rtp_history_ms =
send_config.rtp.nack.rtp_history_ms;
newapi::VideoReceiveStream* receive_stream = call->CreateReceiveStream(
receive_config);
scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer(
test::FrameGeneratorCapturer::Create(
send_stream->Input(),
test::FrameGenerator::Create(
send_config.codec.width, send_config.codec.height,
Clock::GetRealTimeClock()),
30));
receive_stream->StartReceive();
send_stream->StartSend();
frame_generator_capturer->Start();
EXPECT_EQ(kEventSignaled, stream_observer.Wait());
frame_generator_capturer->Stop();
send_stream->StopSend();
receive_stream->StopReceive();
call->DestroyReceiveStream(receive_stream);
call->DestroySendStream(send_stream);
}
INSTANTIATE_TEST_CASE_P(RampUpTest, RampUpTest, ::testing::Bool());
struct EngineTestParams {
size_t width, height;
struct {

View File

@ -14,6 +14,8 @@
'sources': [
'common/direct_transport.cc',
'common/direct_transport.h',
'common/fake_encoder.cc',
'common/fake_encoder.h',
'common/file_capturer.cc',
'common/file_capturer.h',
'common/flags.cc',