Minor improvements to VideoProcessor and corresponding test.

- Make all overridden methods of VideoProcessorImpl public,
  in preparation of the removal of the VideoProcessor interface.
- Place corresponding method definitions in correct order
  in .cc file.
- Harmonize the stdout printing.
- Make timestamp calculations adhere to set frame rate.

Except for the last bullet, these changes should not lead to
different functionality.

BUG=webrtc:6634

Review-Url: https://codereview.webrtc.org/2995513002
Cr-Commit-Position: refs/heads/master@{#19254}
This commit is contained in:
brandtr
2017-08-07 03:36:54 -07:00
committed by Commit Bot
parent 669ea1917e
commit bea36fdee8
6 changed files with 215 additions and 184 deletions

View File

@ -22,7 +22,7 @@ EVENT_START = 'RUN ] CodecSettings/PlotVideoProcessorIntegrationTest.'
EVENT_END = 'OK ] CodecSettings/PlotVideoProcessorIntegrationTest.' EVENT_END = 'OK ] CodecSettings/PlotVideoProcessorIntegrationTest.'
# Metrics to plot, tuple: (name to parse in file, label to use when plotting). # Metrics to plot, tuple: (name to parse in file, label to use when plotting).
BITRATE = ('Target Bitrate', 'target bitrate (kbps)') BITRATE = ('Target bitrate', 'target bitrate (kbps)')
WIDTH = ('Width', 'width') WIDTH = ('Width', 'width')
HEIGHT = ('Height', 'height') HEIGHT = ('Height', 'height')
FILENAME = ('Filename', 'clip') FILENAME = ('Filename', 'clip')
@ -30,18 +30,18 @@ CODEC_TYPE = ('Codec type', 'Codec')
ENCODER_IMPLEMENTATION_NAME = ('Encoder implementation name', 'enc name') ENCODER_IMPLEMENTATION_NAME = ('Encoder implementation name', 'enc name')
DECODER_IMPLEMENTATION_NAME = ('Decoder implementation name', 'dec name') DECODER_IMPLEMENTATION_NAME = ('Decoder implementation name', 'dec name')
CODEC_IMPLEMENTATION_NAME = ('Codec implementation name', 'codec name') CODEC_IMPLEMENTATION_NAME = ('Codec implementation name', 'codec name')
CORES = ('#CPU cores used', 'CPU cores used') CORES = ('# CPU cores used', 'CPU cores used')
DENOISING = ('Denoising', 'denoising') DENOISING = ('Denoising', 'denoising')
RESILIENCE = ('Resilience', 'resilience') RESILIENCE = ('Resilience', 'resilience')
ERROR_CONCEALMENT = ('Error concealment', 'error concealment') ERROR_CONCEALMENT = ('Error concealment', 'error concealment')
QP = ('Average QP', 'avg QP') QP = ('Average QP', 'avg QP')
PSNR = ('PSNR avg', 'PSNR (dB)') PSNR = ('PSNR avg', 'PSNR (dB)')
SSIM = ('SSIM avg', 'SSIM') SSIM = ('SSIM avg', 'SSIM')
ENC_BITRATE = ('Encoding bitrate', 'encoded bitrate (kbps)') ENC_BITRATE = ('Encoded bitrate', 'encoded bitrate (kbps)')
FRAMERATE = ('Frame rate', 'fps') FRAMERATE = ('Frame rate', 'fps')
NUM_FRAMES = ('Number of processed frames', 'num frames') NUM_FRAMES = ('# processed frames', 'num frames')
NUM_DROPPED_FRAMES = ('Number of dropped frames', 'num dropped frames') NUM_DROPPED_FRAMES = ('# dropped frames', 'num dropped frames')
NUM_FRAMES_TO_TARGET = ('Number of frames to approach target rate', NUM_FRAMES_TO_TARGET = ('# frames to convergence',
'frames to reach target rate') 'frames to reach target rate')
ENCODE_TIME = ('Encoding time', 'encode time (us)') ENCODE_TIME = ('Encoding time', 'encode time (us)')
ENCODE_TIME_AVG = ('Encoding time', 'encode time (us) avg') ENCODE_TIME_AVG = ('Encoding time', 'encode time (us) avg')

View File

@ -50,7 +50,6 @@ FrameStatistic& Stats::NewFrame(int frame_number) {
} }
void Stats::PrintSummary() { void Stats::PrintSummary() {
printf("Processing summary:\n");
if (stats_.empty()) { if (stats_.empty()) {
printf("No frame statistics have been logged yet.\n"); printf("No frame statistics have been logged yet.\n");
return; return;
@ -145,22 +144,22 @@ void Stats::PrintSummary() {
} }
// Bitrate stats. // Bitrate stats.
printf("Bit rates:\n"); printf("Bitrates:\n");
frame = std::min_element(stats_.begin(), stats_.end(), LessForBitRate); frame = std::min_element(stats_.begin(), stats_.end(), LessForBitRate);
printf(" Min bit rate: %7d kbps (frame %d)\n", frame->bit_rate_in_kbps, printf(" Min bitrate: %7d kbps (frame %d)\n", frame->bit_rate_in_kbps,
frame->frame_number); frame->frame_number);
frame = std::max_element(stats_.begin(), stats_.end(), LessForBitRate); frame = std::max_element(stats_.begin(), stats_.end(), LessForBitRate);
printf(" Max bit rate: %7d kbps (frame %d)\n", frame->bit_rate_in_kbps, printf(" Max bitrate: %7d kbps (frame %d)\n", frame->bit_rate_in_kbps,
frame->frame_number); frame->frame_number);
int avg_qp = (total_qp_count > 0) ? (total_qp / total_qp_count) : -1;
printf("Average QP: %d\n", avg_qp);
printf("\n"); printf("\n");
printf("Total encoding time : %7d ms.\n", total_encoding_time_in_us / 1000); printf("Total encoding time : %7d ms.\n", total_encoding_time_in_us / 1000);
printf("Total decoding time : %7d ms.\n", total_decoding_time_in_us / 1000); printf("Total decoding time : %7d ms.\n", total_decoding_time_in_us / 1000);
printf("Total processing time: %7d ms.\n", printf("Total processing time: %7d ms.\n",
(total_encoding_time_in_us + total_decoding_time_in_us) / 1000); (total_encoding_time_in_us + total_decoding_time_in_us) / 1000);
int avg_qp = (total_qp_count > 0) ? (total_qp / total_qp_count) : -1;
printf("Average QP: %d\n", avg_qp);
} }
} // namespace test } // namespace test

View File

@ -23,6 +23,7 @@
#include "webrtc/modules/video_coding/include/video_codec_initializer.h" #include "webrtc/modules/video_coding/include/video_codec_initializer.h"
#include "webrtc/modules/video_coding/utility/default_video_bitrate_allocator.h" #include "webrtc/modules/video_coding/utility/default_video_bitrate_allocator.h"
#include "webrtc/rtc_base/checks.h" #include "webrtc/rtc_base/checks.h"
#include "webrtc/rtc_base/logging.h"
#include "webrtc/rtc_base/timeutils.h" #include "webrtc/rtc_base/timeutils.h"
#include "webrtc/system_wrappers/include/cpu_info.h" #include "webrtc/system_wrappers/include/cpu_info.h"
@ -31,22 +32,7 @@ namespace test {
namespace { namespace {
// TODO(brandtr): Update this to use the real frame rate. const int kRtpClockRateHz = 90000;
const int k90khzTimestampFrameDiff = 3000; // Assuming 30 fps.
// Use the frame number as the basis for timestamp to identify frames. Let the
// first timestamp be non-zero, to not make the IvfFileWriter believe that we
// want to use capture timestamps in the IVF files.
uint32_t FrameNumberToTimestamp(int frame_number) {
RTC_DCHECK_GE(frame_number, 0);
return (frame_number + 1) * k90khzTimestampFrameDiff;
}
int TimestampToFrameNumber(uint32_t timestamp) {
RTC_DCHECK_GT(timestamp, 0);
RTC_DCHECK_EQ(timestamp % k90khzTimestampFrameDiff, 0);
return (timestamp / k90khzTimestampFrameDiff) - 1;
}
std::unique_ptr<VideoBitrateAllocator> CreateBitrateAllocator( std::unique_ptr<VideoBitrateAllocator> CreateBitrateAllocator(
const TestConfig& config) { const TestConfig& config) {
@ -60,21 +46,41 @@ std::unique_ptr<VideoBitrateAllocator> CreateBitrateAllocator(
std::move(tl_factory))); std::move(tl_factory)));
} }
void PrintCodecSettings(const VideoCodec* config) { void PrintCodecSettings(const VideoCodec* codec_settings) {
printf(" Start bitrate : %d kbps\n", config->startBitrate); RTC_DCHECK(codec_settings);
printf(" Width : %d\n", config->width); printf(" Codec settings:\n");
printf(" Height : %d\n", config->height); printf(" Codec type : %s\n",
printf(" Codec type : %s\n", CodecTypeToPayloadName(codec_settings->codecType).value_or("Unknown"));
CodecTypeToPayloadName(config->codecType).value_or("Unknown")); printf(" Start bitrate : %d kbps\n", codec_settings->startBitrate);
if (config->codecType == kVideoCodecVP8) { printf(" Max bitrate : %d kbps\n", codec_settings->maxBitrate);
printf(" Denoising : %d\n", config->VP8().denoisingOn); printf(" Min bitrate : %d kbps\n", codec_settings->minBitrate);
printf(" Error concealment: %d\n", config->VP8().errorConcealmentOn); printf(" Width : %d\n", codec_settings->width);
printf(" Frame dropping : %d\n", config->VP8().frameDroppingOn); printf(" Height : %d\n", codec_settings->height);
printf(" Resilience : %d\n", config->VP8().resilience); printf(" Max frame rate : %d\n", codec_settings->maxFramerate);
} else if (config->codecType == kVideoCodecVP9) { printf(" QPmax : %d\n", codec_settings->qpMax);
printf(" Denoising : %d\n", config->VP9().denoisingOn); if (codec_settings->codecType == kVideoCodecVP8) {
printf(" Frame dropping : %d\n", config->VP9().frameDroppingOn); printf(" Complexity : %d\n", codec_settings->VP8().complexity);
printf(" Resilience : %d\n", config->VP9().resilienceOn); printf(" Denoising : %d\n", codec_settings->VP8().denoisingOn);
printf(" Error concealment : %d\n",
codec_settings->VP8().errorConcealmentOn);
printf(" Frame dropping : %d\n", codec_settings->VP8().frameDroppingOn);
printf(" Resilience : %d\n", codec_settings->VP8().resilience);
printf(" Key frame interval: %d\n",
codec_settings->VP8().keyFrameInterval);
} else if (codec_settings->codecType == kVideoCodecVP9) {
printf(" Complexity : %d\n", codec_settings->VP9().complexity);
printf(" Denoising : %d\n", codec_settings->VP9().denoisingOn);
printf(" Frame dropping : %d\n", codec_settings->VP9().frameDroppingOn);
printf(" Resilience : %d\n", codec_settings->VP9().resilienceOn);
printf(" Key frame interval: %d\n",
codec_settings->VP9().keyFrameInterval);
printf(" Adaptive QP mode : %d\n", codec_settings->VP9().adaptiveQpMode);
} else if (codec_settings->codecType == kVideoCodecH264) {
printf(" Frame dropping : %d\n",
codec_settings->H264().frameDroppingOn);
printf(" Key frame interval: %d\n",
codec_settings->H264().keyFrameInterval);
printf(" Profile : %d\n", codec_settings->H264().profile);
} }
} }
@ -138,11 +144,17 @@ VideoProcessorImpl::VideoProcessorImpl(webrtc::VideoEncoder* encoder,
frame_infos_.reserve(analysis_frame_reader->NumberOfFrames()); frame_infos_.reserve(analysis_frame_reader->NumberOfFrames());
} }
VideoProcessorImpl::~VideoProcessorImpl() {
encoder_->RegisterEncodeCompleteCallback(nullptr);
decoder_->RegisterDecodeCompleteCallback(nullptr);
}
void VideoProcessorImpl::Init() { void VideoProcessorImpl::Init() {
RTC_DCHECK(!initialized_) << "VideoProcessor already initialized."; RTC_DCHECK(!initialized_) << "VideoProcessor already initialized.";
RTC_DCHECK(config_.codec_settings) << "No codec settings supplied.";
initialized_ = true; initialized_ = true;
// Setup required callbacks for the encoder/decoder. // Setup required callbacks for the encoder and decoder.
RTC_CHECK_EQ(encoder_->RegisterEncodeCompleteCallback(encode_callback_.get()), RTC_CHECK_EQ(encoder_->RegisterEncodeCompleteCallback(encode_callback_.get()),
WEBRTC_VIDEO_CODEC_OK) WEBRTC_VIDEO_CODEC_OK)
<< "Failed to register encode complete callback"; << "Failed to register encode complete callback";
@ -164,70 +176,26 @@ void VideoProcessorImpl::Init() {
<< "Failed to initialize VideoDecoder"; << "Failed to initialize VideoDecoder";
if (config_.verbose) { if (config_.verbose) {
printf("Filename: %s\n", config_.filename.c_str());
printf("Video Processor:\n"); printf("Video Processor:\n");
printf(" #CPU cores used : %d\n", num_cores); printf(" Filename : %s\n", config_.filename.c_str());
printf(" Total # of frames: %d\n", printf(" Total # of frames: %d\n",
analysis_frame_reader_->NumberOfFrames()); analysis_frame_reader_->NumberOfFrames());
printf(" Codec settings:\n"); printf(" # CPU cores used : %d\n", num_cores);
printf(" Encoder implementation name: %s\n", const char* encoder_name = encoder_->ImplementationName();
encoder_->ImplementationName()); printf(" Encoder implementation name: %s\n", encoder_name);
printf(" Decoder implementation name: %s\n", const char* decoder_name = decoder_->ImplementationName();
decoder_->ImplementationName()); printf(" Decoder implementation name: %s\n", decoder_name);
if (strcmp(encoder_->ImplementationName(), if (strcmp(encoder_name, decoder_name) == 0) {
decoder_->ImplementationName()) == 0) { printf(" Codec implementation name : %s_%s\n",
printf(" Codec implementation name: %s_%s\n",
CodecTypeToPayloadName(config_.codec_settings->codecType) CodecTypeToPayloadName(config_.codec_settings->codecType)
.value_or("Unknown"), .value_or("Unknown"),
encoder_->ImplementationName()); encoder_->ImplementationName());
} }
PrintCodecSettings(config_.codec_settings); PrintCodecSettings(config_.codec_settings);
printf("\n");
} }
} }
VideoProcessorImpl::~VideoProcessorImpl() {
encoder_->RegisterEncodeCompleteCallback(nullptr);
decoder_->RegisterDecodeCompleteCallback(nullptr);
}
void VideoProcessorImpl::SetRates(int bit_rate, int frame_rate) {
int set_rates_result = encoder_->SetRateAllocation(
bitrate_allocator_->GetAllocation(bit_rate * 1000, frame_rate),
frame_rate);
RTC_DCHECK_GE(set_rates_result, 0)
<< "Failed to update encoder with new rate " << bit_rate;
num_dropped_frames_ = 0;
num_spatial_resizes_ = 0;
}
size_t VideoProcessorImpl::EncodedFrameSize(int frame_number) {
RTC_DCHECK_LT(frame_number, frame_infos_.size());
return frame_infos_[frame_number].encoded_frame_size;
}
FrameType VideoProcessorImpl::EncodedFrameType(int frame_number) {
RTC_DCHECK_LT(frame_number, frame_infos_.size());
return frame_infos_[frame_number].encoded_frame_type;
}
int VideoProcessorImpl::GetQpFromEncoder(int frame_number) {
RTC_DCHECK_LT(frame_number, frame_infos_.size());
return frame_infos_[frame_number].qp_encoder;
}
int VideoProcessorImpl::GetQpFromBitstream(int frame_number) {
RTC_DCHECK_LT(frame_number, frame_infos_.size());
return frame_infos_[frame_number].qp_bitstream;
}
int VideoProcessorImpl::NumberDroppedFrames() {
return num_dropped_frames_;
}
int VideoProcessorImpl::NumberSpatialResizes() {
return num_spatial_resizes_;
}
bool VideoProcessorImpl::ProcessFrame(int frame_number) { bool VideoProcessorImpl::ProcessFrame(int frame_number) {
RTC_DCHECK_GE(frame_number, 0); RTC_DCHECK_GE(frame_number, 0);
RTC_DCHECK_LE(frame_number, frame_infos_.size()) RTC_DCHECK_LE(frame_number, frame_infos_.size())
@ -277,13 +245,53 @@ bool VideoProcessorImpl::ProcessFrame(int frame_number) {
encoder_->Encode(source_frame, nullptr, &frame_types); encoder_->Encode(source_frame, nullptr, &frame_types);
if (frame_stat->encode_return_code != WEBRTC_VIDEO_CODEC_OK) { if (frame_stat->encode_return_code != WEBRTC_VIDEO_CODEC_OK) {
fprintf(stderr, "Failed to encode frame %d, return code: %d\n", LOG(LS_WARNING) << "Failed to encode frame " << frame_number
frame_number, frame_stat->encode_return_code); << ", return code: " << frame_stat->encode_return_code
<< ".";
} }
return true; return true;
} }
void VideoProcessorImpl::SetRates(int bit_rate, int frame_rate) {
config_.codec_settings->maxFramerate = frame_rate;
int set_rates_result = encoder_->SetRateAllocation(
bitrate_allocator_->GetAllocation(bit_rate * 1000, frame_rate),
frame_rate);
RTC_DCHECK_GE(set_rates_result, 0)
<< "Failed to update encoder with new rate " << bit_rate;
num_dropped_frames_ = 0;
num_spatial_resizes_ = 0;
}
size_t VideoProcessorImpl::EncodedFrameSize(int frame_number) {
RTC_DCHECK_LT(frame_number, frame_infos_.size());
return frame_infos_[frame_number].encoded_frame_size;
}
FrameType VideoProcessorImpl::EncodedFrameType(int frame_number) {
RTC_DCHECK_LT(frame_number, frame_infos_.size());
return frame_infos_[frame_number].encoded_frame_type;
}
int VideoProcessorImpl::GetQpFromEncoder(int frame_number) {
RTC_DCHECK_LT(frame_number, frame_infos_.size());
return frame_infos_[frame_number].qp_encoder;
}
int VideoProcessorImpl::GetQpFromBitstream(int frame_number) {
RTC_DCHECK_LT(frame_number, frame_infos_.size());
return frame_infos_[frame_number].qp_bitstream;
}
int VideoProcessorImpl::NumberDroppedFrames() {
return num_dropped_frames_;
}
int VideoProcessorImpl::NumberSpatialResizes() {
return num_spatial_resizes_;
}
void VideoProcessorImpl::FrameEncoded( void VideoProcessorImpl::FrameEncoded(
webrtc::VideoCodecType codec, webrtc::VideoCodecType codec,
const EncodedImage& encoded_image, const EncodedImage& encoded_image,
@ -488,5 +496,20 @@ void VideoProcessorImpl::FrameDecoded(const VideoFrame& image) {
last_decoded_frame_buffer_ = std::move(extracted_buffer); last_decoded_frame_buffer_ = std::move(extracted_buffer);
} }
uint32_t VideoProcessorImpl::FrameNumberToTimestamp(int frame_number) {
RTC_DCHECK_GE(frame_number, 0);
const int ticks_per_frame =
kRtpClockRateHz / config_.codec_settings->maxFramerate;
return (frame_number + 1) * ticks_per_frame;
}
int VideoProcessorImpl::TimestampToFrameNumber(uint32_t timestamp) {
RTC_DCHECK_GT(timestamp, 0);
const int ticks_per_frame =
kRtpClockRateHz / config_.codec_settings->maxFramerate;
RTC_DCHECK_EQ(timestamp % ticks_per_frame, 0);
return (timestamp / ticks_per_frame) - 1;
}
} // namespace test } // namespace test
} // namespace webrtc } // namespace webrtc

View File

@ -136,6 +136,8 @@ struct TestConfig {
// //
// Note this class is not thread safe in any way and is meant for simple testing // Note this class is not thread safe in any way and is meant for simple testing
// purposes. // purposes.
//
// TODO(brandtr): Remove this interface.
class VideoProcessor { class VideoProcessor {
public: public:
virtual ~VideoProcessor() {} virtual ~VideoProcessor() {}
@ -183,9 +185,18 @@ class VideoProcessorImpl : public VideoProcessor {
FrameWriter* source_frame_writer, FrameWriter* source_frame_writer,
IvfFileWriter* encoded_frame_writer, IvfFileWriter* encoded_frame_writer,
FrameWriter* decoded_frame_writer); FrameWriter* decoded_frame_writer);
virtual ~VideoProcessorImpl(); ~VideoProcessorImpl() override;
// Implements VideoProcessor.
void Init() override; void Init() override;
bool ProcessFrame(int frame_number) override; bool ProcessFrame(int frame_number) override;
void SetRates(int bit_rate, int frame_rate) override;
size_t EncodedFrameSize(int frame_number) override;
FrameType EncodedFrameType(int frame_number) override;
int GetQpFromEncoder(int frame_number) override;
int GetQpFromBitstream(int frame_number) override;
int NumberDroppedFrames() override;
int NumberSpatialResizes() override;
private: private:
// Container that holds per-frame information that needs to be stored between // Container that holds per-frame information that needs to be stored between
@ -255,8 +266,7 @@ class VideoProcessorImpl : public VideoProcessor {
void Decoded(webrtc::VideoFrame& image, void Decoded(webrtc::VideoFrame& image,
rtc::Optional<int32_t> decode_time_ms, rtc::Optional<int32_t> decode_time_ms,
rtc::Optional<uint8_t> qp) override { rtc::Optional<uint8_t> qp) override {
Decoded(image, Decoded(image);
decode_time_ms ? static_cast<int32_t>(*decode_time_ms) : -1);
} }
private: private:
@ -271,26 +281,11 @@ class VideoProcessorImpl : public VideoProcessor {
// Invoked by the callback when a frame has completed decoding. // Invoked by the callback when a frame has completed decoding.
void FrameDecoded(const webrtc::VideoFrame& image); void FrameDecoded(const webrtc::VideoFrame& image);
// Updates the encoder with the target bit rate and the frame rate. // Use the frame number as the basis for timestamp to identify frames. Let the
void SetRates(int bit_rate, int frame_rate) override; // first timestamp be non-zero, to not make the IvfFileWriter believe that we
// want to use capture timestamps in the IVF files.
// Return the size of the encoded frame in bytes. uint32_t FrameNumberToTimestamp(int frame_number);
size_t EncodedFrameSize(int frame_number) override; int TimestampToFrameNumber(uint32_t timestamp);
// Return the encoded frame type (key or delta).
FrameType EncodedFrameType(int frame_number) override;
// Return the qp used by encoder.
int GetQpFromEncoder(int frame_number) override;
// Return the qp from the qp parser.
int GetQpFromBitstream(int frame_number) override;
// Return the number of dropped frames.
int NumberDroppedFrames() override;
// Return the number of spatial resizes.
int NumberSpatialResizes() override;
webrtc::VideoEncoder* const encoder_; webrtc::VideoEncoder* const encoder_;
webrtc::VideoDecoder* const decoder_; webrtc::VideoDecoder* const decoder_;

View File

@ -231,7 +231,6 @@ class VideoProcessorIntegrationTest : public testing::Test {
"_cd-" + CodecTypeToPayloadName( "_cd-" + CodecTypeToPayloadName(
config_.codec_settings->codecType).value_or("") + config_.codec_settings->codecType).value_or("") +
"_hw-" + std::to_string(config_.hw_codec) + "_hw-" + std::to_string(config_.hw_codec) +
"_fr-" + std::to_string(start_frame_rate_) +
"_br-" + std::to_string( "_br-" + std::to_string(
static_cast<int>(config_.codec_settings->startBitrate)); static_cast<int>(config_.codec_settings->startBitrate));
// clang-format on // clang-format on
@ -343,16 +342,16 @@ class VideoProcessorIntegrationTest : public testing::Test {
int num_dropped_frames = processor_->NumberDroppedFrames(); int num_dropped_frames = processor_->NumberDroppedFrames();
int num_resize_actions = processor_->NumberSpatialResizes(); int num_resize_actions = processor_->NumberSpatialResizes();
printf( printf(
"For update #: %d,\n" "Rate update #%d:\n"
" Target Bitrate: %d,\n" " Target bitrate : %d\n"
" Encoding bitrate: %f,\n" " Encoded bitrate : %f\n"
" Frame rate: %d \n", " Frame rate : %d\n",
update_index, bit_rate_, encoding_bitrate_total_, frame_rate_); update_index, bit_rate_, encoding_bitrate_total_, frame_rate_);
printf( printf(
" Number of processed frames: %d, \n" " # processed frames : %d\n"
" Number of frames to approach target rate: %d, \n" " # frames to convergence: %d\n"
" Number of dropped frames: %d, \n" " # dropped frames : %d\n"
" Number of spatial resizes: %d, \n", " # spatial resizes : %d\n",
num_frames_total_, num_frames_to_hit_target_, num_dropped_frames, num_frames_total_, num_frames_to_hit_target_, num_dropped_frames,
num_resize_actions); num_resize_actions);
EXPECT_LE(perc_encoding_rate_mismatch_, EXPECT_LE(perc_encoding_rate_mismatch_,
@ -361,29 +360,27 @@ class VideoProcessorIntegrationTest : public testing::Test {
int perc_key_frame_size_mismatch = int perc_key_frame_size_mismatch =
100 * sum_key_frame_size_mismatch_ / num_key_frames_; 100 * sum_key_frame_size_mismatch_ / num_key_frames_;
printf( printf(
" Number of Key frames: %d \n" " # key frames : %d\n"
" Key frame rate mismatch: %d \n", " Key frame rate mismatch: %d\n",
num_key_frames_, perc_key_frame_size_mismatch); num_key_frames_, perc_key_frame_size_mismatch);
EXPECT_LE(perc_key_frame_size_mismatch, EXPECT_LE(perc_key_frame_size_mismatch,
rc_expected.max_key_frame_size_mismatch); rc_expected.max_key_frame_size_mismatch);
} }
printf("\n");
printf("Rates statistics for Layer data \n");
for (int i = 0; i < num_temporal_layers_; i++) { for (int i = 0; i < num_temporal_layers_; i++) {
printf("Temporal layer #%d \n", i); printf(" Temporal layer #%d:\n", i);
int perc_frame_size_mismatch = int perc_frame_size_mismatch =
100 * sum_frame_size_mismatch_[i] / num_frames_per_update_[i]; 100 * sum_frame_size_mismatch_[i] / num_frames_per_update_[i];
int perc_encoding_rate_mismatch = int perc_encoding_rate_mismatch =
100 * fabs(encoding_bitrate_[i] - bit_rate_layer_[i]) / 100 * fabs(encoding_bitrate_[i] - bit_rate_layer_[i]) /
bit_rate_layer_[i]; bit_rate_layer_[i];
printf( printf(
" Target Layer Bit rate: %f \n" " Target layer bitrate : %f\n"
" Layer frame rate: %f, \n" " Layer frame rate : %f\n"
" Layer per frame bandwidth: %f, \n" " Layer per frame bandwidth : %f\n"
" Layer Encoding bit rate: %f, \n" " Layer encoding bitrate : %f\n"
" Layer Percent frame size mismatch: %d, \n" " Layer percent frame size mismatch : %d\n"
" Layer Percent encoding rate mismatch: %d, \n" " Layer percent encoding rate mismatch: %d\n"
" Number of frame processed per layer: %d \n", " # frame processed per layer : %d\n",
bit_rate_layer_[i], frame_rate_layer_[i], per_frame_bandwidth_[i], bit_rate_layer_[i], frame_rate_layer_[i], per_frame_bandwidth_[i],
encoding_bitrate_[i], perc_frame_size_mismatch, encoding_bitrate_[i], perc_frame_size_mismatch,
perc_encoding_rate_mismatch, num_frames_per_update_[i]); perc_encoding_rate_mismatch, num_frames_per_update_[i]);
@ -601,11 +598,12 @@ class VideoProcessorIntegrationTest : public testing::Test {
config_.codec_settings->width, config_.codec_settings->width,
config_.codec_settings->height, config_.codec_settings->height,
&psnr_result, &ssim_result)); &psnr_result, &ssim_result));
VerifyQuality(psnr_result, ssim_result, quality_thresholds);
stats_.PrintSummary();
printf("PSNR avg: %f, min: %f\nSSIM avg: %f, min: %f\n", printf("PSNR avg: %f, min: %f\nSSIM avg: %f, min: %f\n",
psnr_result.average, psnr_result.min, ssim_result.average, psnr_result.average, psnr_result.min, ssim_result.average,
ssim_result.min); ssim_result.min);
VerifyQuality(psnr_result, ssim_result, quality_thresholds); printf("\n");
stats_.PrintSummary();
// Remove analysis file. // Remove analysis file.
if (remove(config_.output_filename.c_str()) < 0) { if (remove(config_.output_filename.c_str()) < 0) {

View File

@ -8,11 +8,14 @@
* be found in the AUTHORS file in the root of the source tree. * be found in the AUTHORS file in the root of the source tree.
*/ */
#include <memory>
#include "webrtc/api/video/i420_buffer.h" #include "webrtc/api/video/i420_buffer.h"
#include "webrtc/modules/video_coding/codecs/test/mock/mock_packet_manipulator.h" #include "webrtc/modules/video_coding/codecs/test/mock/mock_packet_manipulator.h"
#include "webrtc/modules/video_coding/codecs/test/videoprocessor.h" #include "webrtc/modules/video_coding/codecs/test/videoprocessor.h"
#include "webrtc/modules/video_coding/include/mock/mock_video_codec_interface.h" #include "webrtc/modules/video_coding/include/mock/mock_video_codec_interface.h"
#include "webrtc/modules/video_coding/include/video_coding.h" #include "webrtc/modules/video_coding/include/video_coding.h"
#include "webrtc/rtc_base/ptr_util.h"
#include "webrtc/test/gmock.h" #include "webrtc/test/gmock.h"
#include "webrtc/test/gtest.h" #include "webrtc/test/gtest.h"
#include "webrtc/test/testsupport/mock/mock_frame_reader.h" #include "webrtc/test/testsupport/mock/mock_frame_reader.h"
@ -28,30 +31,37 @@ using ::testing::Return;
namespace webrtc { namespace webrtc {
namespace test { namespace test {
// Very basic testing for VideoProcessor. It's mostly tested by running the namespace {
// video_quality_measurement program.
const int kWidth = 352;
const int kHeight = 288;
const int kFrameSize = kWidth * kHeight * 3 / 2; // I420.
const int kFramerate = 30;
const int kNumFrames = 2;
} // namespace
class VideoProcessorTest : public testing::Test { class VideoProcessorTest : public testing::Test {
protected: protected:
MockVideoEncoder encoder_mock_; VideoProcessorTest() {
MockVideoDecoder decoder_mock_;
MockFrameReader frame_reader_mock_;
MockFrameWriter frame_writer_mock_;
MockPacketManipulator packet_manipulator_mock_;
Stats stats_;
TestConfig config_;
VideoCodec codec_settings_;
VideoProcessorTest() {}
virtual ~VideoProcessorTest() {}
void SetUp() {
// Get a codec configuration struct and configure it. // Get a codec configuration struct and configure it.
VideoCodingModule::Codec(kVideoCodecVP8, &codec_settings_); VideoCodingModule::Codec(kVideoCodecVP8, &codec_settings_);
config_.codec_settings = &codec_settings_; config_.codec_settings = &codec_settings_;
config_.codec_settings->startBitrate = 100; config_.codec_settings->startBitrate = 100;
config_.codec_settings->width = 352; config_.codec_settings->width = kWidth;
config_.codec_settings->height = 288; config_.codec_settings->height = kHeight;
config_.codec_settings->maxFramerate = kFramerate;
EXPECT_CALL(frame_reader_mock_, NumberOfFrames())
.WillRepeatedly(Return(kNumFrames));
EXPECT_CALL(frame_reader_mock_, FrameLength())
.WillRepeatedly(Return(kFrameSize));
video_processor_ = rtc::MakeUnique<VideoProcessorImpl>(
&encoder_mock_, &decoder_mock_, &frame_reader_mock_,
&frame_writer_mock_, &packet_manipulator_mock_, config_, &stats_,
nullptr /* source_frame_writer */, nullptr /* encoded_frame_writer */,
nullptr /* decoded_frame_writer */);
} }
void TearDown() {}
void ExpectInit() { void ExpectInit() {
EXPECT_CALL(encoder_mock_, InitEncode(_, _, _)).Times(1); EXPECT_CALL(encoder_mock_, InitEncode(_, _, _)).Times(1);
@ -60,35 +70,41 @@ class VideoProcessorTest : public testing::Test {
EXPECT_CALL(decoder_mock_, InitDecode(_, _)).Times(1); EXPECT_CALL(decoder_mock_, InitDecode(_, _)).Times(1);
EXPECT_CALL(decoder_mock_, RegisterDecodeCompleteCallback(_)) EXPECT_CALL(decoder_mock_, RegisterDecodeCompleteCallback(_))
.Times(AtLeast(1)); .Times(AtLeast(1));
EXPECT_CALL(frame_reader_mock_, NumberOfFrames()).WillRepeatedly(Return(1));
EXPECT_CALL(frame_reader_mock_, FrameLength()).WillOnce(Return(152064));
} }
MockVideoEncoder encoder_mock_;
MockVideoDecoder decoder_mock_;
MockFrameReader frame_reader_mock_;
MockFrameWriter frame_writer_mock_;
MockPacketManipulator packet_manipulator_mock_;
VideoCodec codec_settings_;
TestConfig config_;
Stats stats_;
std::unique_ptr<VideoProcessorImpl> video_processor_;
}; };
TEST_F(VideoProcessorTest, Init) { TEST_F(VideoProcessorTest, Init) {
ExpectInit(); ExpectInit();
VideoProcessorImpl video_processor( video_processor_->Init();
&encoder_mock_, &decoder_mock_, &frame_reader_mock_, &frame_writer_mock_,
&packet_manipulator_mock_, config_, &stats_,
nullptr /* source_frame_writer */, nullptr /* encoded_frame_writer */,
nullptr /* decoded_frame_writer */);
video_processor.Init();
} }
TEST_F(VideoProcessorTest, ProcessFrame) { TEST_F(VideoProcessorTest, ProcessFrames) {
ExpectInit(); ExpectInit();
EXPECT_CALL(encoder_mock_, Encode(_, _, _)).Times(1); video_processor_->Init();
EXPECT_CALL(frame_reader_mock_, ReadFrame()) EXPECT_CALL(frame_reader_mock_, ReadFrame())
.WillOnce(Return(I420Buffer::Create(50, 50))); .WillRepeatedly(Return(I420Buffer::Create(kWidth, kHeight)));
// Since we don't return any callback from the mock, the decoder will not EXPECT_CALL(encoder_mock_, Encode(testing::Property(&VideoFrame::timestamp,
// be more than initialized... 1 * 90000 / kFramerate),
VideoProcessorImpl video_processor( _, _))
&encoder_mock_, &decoder_mock_, &frame_reader_mock_, &frame_writer_mock_, .Times(1);
&packet_manipulator_mock_, config_, &stats_, video_processor_->ProcessFrame(0);
nullptr /* source_frame_writer */, nullptr /* encoded_frame_writer */,
nullptr /* decoded_frame_writer */); EXPECT_CALL(encoder_mock_, Encode(testing::Property(&VideoFrame::timestamp,
video_processor.Init(); 2 * 90000 / kFramerate),
video_processor.ProcessFrame(0); _, _))
.Times(1);
video_processor_->ProcessFrame(1);
} }
} // namespace test } // namespace test