brandtr b2def1d06f Add batch mode to VideoProcessor integration tests.
Prior to this CL, the encoding/decoding in the VideoProcessor integration
tests were run "online", in the sense that rate allocations could be
changed in between frames. This is useful for evaluating the rate control
of SW codecs, which is one of the reasons for the existence of these
integration tests in the first place.

This CL adds a batch mode, in which the tests are run "offline". The two
main differences to the original mode are: 1) rate control metrics are
calculated after the fact, and 2) no rate allocation changes are allowed
during the test. Difference 1) is the reason for this CL, as HW codecs
that are pipelining will not work well when rate control metrics are
calculated right after a frame has been sent for encode. Difference 2)
is a side effect of the introduction of the batch mode. If we want to
be able to support online rate allocation for pipelining HW codecs in
the future, this can be introduced by adding a delay between encoding
and rate allocation. This was not deemed necessary at this point in time,
and hence this CL does not do that.

The batch mode is only intended to be used for manual experimentation
on devices with HW codecs, and the integration tests running on the
bots should thus NOT use batch mode.

BUG=webrtc:6634

Review-Url: https://codereview.webrtc.org/2707023008
Cr-Commit-Position: refs/heads/master@{#17164}
2017-03-10 12:20:10 +00:00

328 lines
12 KiB
C++

/*
* Copyright (c) 2012 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_TEST_VIDEOPROCESSOR_H_
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_VIDEOPROCESSOR_H_
#include <memory>
#include <string>
#include <vector>
#include "webrtc/api/video/video_frame.h"
#include "webrtc/base/buffer.h"
#include "webrtc/base/checks.h"
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
#include "webrtc/modules/video_coding/codecs/test/packet_manipulator.h"
#include "webrtc/modules/video_coding/codecs/test/stats.h"
#include "webrtc/modules/video_coding/utility/ivf_file_writer.h"
#include "webrtc/test/testsupport/frame_reader.h"
#include "webrtc/test/testsupport/frame_writer.h"
namespace webrtc {
class VideoBitrateAllocator;
namespace test {
// Defines which frame types shall be excluded from packet loss and when.
enum ExcludeFrameTypes {
// Will exclude the first keyframe in the video sequence from packet loss.
// Following keyframes will be targeted for packet loss.
kExcludeOnlyFirstKeyFrame,
// Exclude all keyframes from packet loss, no matter where in the video
// sequence they occur.
kExcludeAllKeyFrames
};
// Returns a string representation of the enum value.
const char* ExcludeFrameTypesToStr(ExcludeFrameTypes e);
// Test configuration for a test run.
struct TestConfig {
TestConfig();
~TestConfig();
// Name of the test. This is purely metadata and does not affect
// the test in any way.
std::string name;
// More detailed description of the test. This is purely metadata and does
// not affect the test in any way.
std::string description;
// Number of this test. Useful if multiple runs of the same test with
// different configurations shall be managed.
int test_number;
// File to process for the test. This must be a video file in the YUV format.
std::string input_filename;
// File to write to during processing for the test. Will be a video file
// in the YUV format.
std::string output_filename;
// Path to the directory where encoded files will be put
// (absolute or relative to the executable). Default: "out".
std::string output_dir;
// Configurations related to networking.
NetworkingConfig networking_config;
// Decides how the packet loss simulations shall exclude certain frames
// from packet loss. Default: kExcludeOnlyFirstKeyFrame.
ExcludeFrameTypes exclude_frame_types;
// The length of a single frame of the input video file. This value is
// calculated out of the width and height according to the video format
// specification. Must be set before processing.
size_t frame_length_in_bytes;
// Force the encoder and decoder to use a single core for processing.
// Using a single core is necessary to get a deterministic behavior for the
// encoded frames - using multiple cores will produce different encoded frames
// since multiple cores are competing to consume the byte budget for each
// frame in parallel.
// If set to false, the maximum number of available cores will be used.
// Default: false.
bool use_single_core;
// If set to a value >0 this setting forces the encoder to create a keyframe
// every Nth frame. Note that the encoder may create a keyframe in other
// locations in addition to the interval that is set using this parameter.
// Forcing key frames may also affect encoder planning optimizations in
// a negative way, since it will suddenly be forced to produce an expensive
// key frame.
// Default: 0.
int keyframe_interval;
// The codec settings to use for the test (target bitrate, video size,
// framerate and so on). This struct must be created and filled in using
// the VideoCodingModule::Codec() method.
webrtc::VideoCodec* codec_settings;
// If printing of information to stdout shall be performed during processing.
bool verbose;
};
// Handles encoding/decoding of video using the VideoEncoder/VideoDecoder
// interfaces. This is done in a sequential manner in order to be able to
// measure times properly.
// The class processes a frame at the time for the configured input file.
// It maintains state of where in the source input file the processing is at.
//
// Regarding packet loss: Note that keyframes are excluded (first or all
// depending on the ExcludeFrameTypes setting). This is because if key frames
// would be altered, all the following delta frames would be pretty much
// worthless. VP8 has an error-resilience feature that makes it able to handle
// packet loss in key non-first keyframes, which is why only the first is
// excluded by default.
// Packet loss in such important frames is handled on a higher level in the
// Video Engine, where signaling would request a retransmit of the lost packets,
// since they're so important.
//
// Note this class is not thread safe in any way and is meant for simple testing
// purposes.
class VideoProcessor {
public:
virtual ~VideoProcessor() {}
// Performs initial calculations about frame size, sets up callbacks etc.
// Returns false if an error has occurred, in addition to printing to stderr.
virtual bool Init() = 0;
// Processes a single frame. Returns true as long as there's more frames
// available in the source clip.
// Frame number must be an integer >= 0.
virtual bool ProcessFrame(int frame_number) = 0;
// Updates the encoder with the target bit rate and the frame rate.
virtual void SetRates(int bit_rate, int frame_rate) = 0;
// Return the size of the encoded frame in bytes. Dropped frames by the
// encoder are regarded as zero size.
virtual size_t EncodedFrameSize(int frame_number) = 0;
// Return the encoded frame type (key or delta).
virtual FrameType EncodedFrameType(int frame_number) = 0;
// Return the number of dropped frames.
virtual int NumberDroppedFrames() = 0;
// Return the number of spatial resizes.
virtual int NumberSpatialResizes() = 0;
};
class VideoProcessorImpl : public VideoProcessor {
public:
VideoProcessorImpl(webrtc::VideoEncoder* encoder,
webrtc::VideoDecoder* decoder,
FrameReader* analysis_frame_reader,
FrameWriter* analysis_frame_writer,
PacketManipulator* packet_manipulator,
const TestConfig& config,
Stats* stats,
FrameWriter* source_frame_writer,
IvfFileWriter* encoded_frame_writer,
FrameWriter* decoded_frame_writer);
virtual ~VideoProcessorImpl();
bool Init() override;
bool ProcessFrame(int frame_number) override;
private:
// Container that holds per-frame information that needs to be stored between
// calls to Encode and Decode, as well as the corresponding callbacks. It is
// not directly used for statistics -- for that, test::FrameStatistic is used.
struct FrameInfo {
FrameInfo()
: timestamp(0),
encode_start_ns(0),
decode_start_ns(0),
encoded_frame_size(0),
encoded_frame_type(kVideoFrameDelta),
decoded_width(0),
decoded_height(0),
manipulated_length(0) {}
uint32_t timestamp;
int64_t encode_start_ns;
int64_t decode_start_ns;
size_t encoded_frame_size;
FrameType encoded_frame_type;
int decoded_width;
int decoded_height;
size_t manipulated_length;
};
// Callback class required to implement according to the VideoEncoder API.
class VideoProcessorEncodeCompleteCallback
: public webrtc::EncodedImageCallback {
public:
explicit VideoProcessorEncodeCompleteCallback(VideoProcessorImpl* vp)
: video_processor_(vp) {}
Result OnEncodedImage(
const webrtc::EncodedImage& encoded_image,
const webrtc::CodecSpecificInfo* codec_specific_info,
const webrtc::RTPFragmentationHeader* fragmentation) override {
// Forward to parent class.
RTC_CHECK(codec_specific_info);
video_processor_->FrameEncoded(codec_specific_info->codecType,
encoded_image, fragmentation);
return Result(Result::OK, 0);
}
private:
VideoProcessorImpl* const video_processor_;
};
// Callback class required to implement according to the VideoDecoder API.
class VideoProcessorDecodeCompleteCallback
: public webrtc::DecodedImageCallback {
public:
explicit VideoProcessorDecodeCompleteCallback(VideoProcessorImpl* vp)
: video_processor_(vp) {}
int32_t Decoded(webrtc::VideoFrame& image) override {
// Forward to parent class.
video_processor_->FrameDecoded(image);
return 0;
}
int32_t Decoded(webrtc::VideoFrame& image,
int64_t decode_time_ms) override {
return Decoded(image);
}
void Decoded(webrtc::VideoFrame& image,
rtc::Optional<int32_t> decode_time_ms,
rtc::Optional<uint8_t> qp) override {
Decoded(image,
decode_time_ms ? static_cast<int32_t>(*decode_time_ms) : -1);
}
private:
VideoProcessorImpl* const video_processor_;
};
// Invoked by the callback when a frame has completed encoding.
void FrameEncoded(webrtc::VideoCodecType codec,
const webrtc::EncodedImage& encodedImage,
const webrtc::RTPFragmentationHeader* fragmentation);
// Invoked by the callback when a frame has completed decoding.
void FrameDecoded(const webrtc::VideoFrame& image);
// Updates the encoder with the target bit rate and the frame rate.
void SetRates(int bit_rate, int frame_rate) override;
// Return the size of the encoded frame in bytes.
size_t EncodedFrameSize(int frame_number) override;
// Return the encoded frame type (key or delta).
FrameType EncodedFrameType(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::VideoDecoder* const decoder_;
const std::unique_ptr<VideoBitrateAllocator> bitrate_allocator_;
// Adapters for the codec callbacks.
const std::unique_ptr<EncodedImageCallback> encode_callback_;
const std::unique_ptr<DecodedImageCallback> decode_callback_;
PacketManipulator* const packet_manipulator_;
const TestConfig& config_;
// These (mandatory) file manipulators are used for, e.g., objective PSNR and
// SSIM calculations at the end of a test run.
FrameReader* const analysis_frame_reader_;
FrameWriter* const analysis_frame_writer_;
const int num_frames_;
// These (optional) file writers are used for persistently storing the output
// of the coding pipeline at different stages: pre encode (source), post
// encode (encoded), and post decode (decoded). The purpose is to give the
// experimenter an option to subjectively evaluate the quality of the
// encoding, given the test settings. Each frame writer is enabled by being
// non-null.
FrameWriter* const source_frame_writer_;
IvfFileWriter* const encoded_frame_writer_;
FrameWriter* const decoded_frame_writer_;
bool initialized_;
// Frame metadata for all frames that have been added through a call to
// ProcessFrames(). We need to store this metadata over the course of the
// test run, to support pipelining HW codecs.
std::vector<FrameInfo> frame_infos_;
int last_encoded_frame_num_;
int last_decoded_frame_num_;
// Keep track of if we have excluded the first key frame from packet loss.
bool first_key_frame_has_been_excluded_;
// Keep track of the last successfully decoded frame, since we write that
// frame to disk when decoding fails.
rtc::Buffer last_decoded_frame_buffer_;
// Statistics.
Stats* stats_;
int num_dropped_frames_;
int num_spatial_resizes_;
double bit_rate_factor_; // Multiply frame length with this to get bit rate.
};
} // namespace test
} // namespace webrtc
#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_VIDEOPROCESSOR_H_