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:
@ -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')
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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_;
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user