First version of video quality measurement program and test framework.
See https://docs.google.com/a/google.com/document/d/1w6Nrxw6yTg_sDu18Ux8oZPEMo5F_R-zt62udrmmTeOc/edit?hl=en_US for background, details and additional instructions on usage. BUG= TEST= Review URL: http://webrtc-codereview.appspot.com/175001 git-svn-id: http://webrtc.googlecode.com/svn/trunk@700 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
3ce62fcfe4
commit
35a1756502
@ -52,6 +52,8 @@
|
||||
'rtp_rtcp/test/test_bwe/test_bwe.gypi',
|
||||
'rtp_rtcp/test/testFec/test_fec.gypi',
|
||||
'video_coding/main/source/video_coding_test.gypi',
|
||||
'video_coding/codecs/test/video_codecs_test_framework.gypi',
|
||||
'video_coding/codecs/tools/video_codecs_tools.gypi',
|
||||
'video_processing/main/test/vpm_tests.gypi',
|
||||
], # includes
|
||||
}], # build_with_chromium
|
||||
|
113
src/modules/video_coding/codecs/test/file_handler.cc
Normal file
113
src/modules/video_coding/codecs/test/file_handler.cc
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 "file_handler.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
FileHandlerImpl::FileHandlerImpl(std::string input_filename,
|
||||
std::string output_filename,
|
||||
int frame_length_in_bytes)
|
||||
: input_filename_(input_filename),
|
||||
output_filename_(output_filename),
|
||||
frame_length_in_bytes_(frame_length_in_bytes),
|
||||
input_file_(NULL),
|
||||
output_file_(NULL) {
|
||||
}
|
||||
|
||||
FileHandlerImpl::~FileHandlerImpl() {
|
||||
Close();
|
||||
}
|
||||
|
||||
bool FileHandlerImpl::Init() {
|
||||
if (frame_length_in_bytes_ <= 0) {
|
||||
fprintf(stderr, "Frame length must be >0, was %d\n",
|
||||
frame_length_in_bytes_);
|
||||
return false;
|
||||
}
|
||||
|
||||
input_file_ = fopen(input_filename_.c_str(), "rb");
|
||||
if (input_file_ == NULL) {
|
||||
fprintf(stderr, "Couldn't open input file for reading: %s\n",
|
||||
input_filename_.c_str());
|
||||
return false;
|
||||
}
|
||||
output_file_ = fopen(output_filename_.c_str(), "wb");
|
||||
if (output_file_ == NULL) {
|
||||
fprintf(stderr, "Couldn't open output file for writing: %s\n",
|
||||
output_filename_.c_str());
|
||||
return false;
|
||||
}
|
||||
// Calculate total number of frames:
|
||||
WebRtc_UWord64 source_file_size = GetFileSize(input_filename_);
|
||||
if (source_file_size <= 0u) {
|
||||
fprintf(stderr, "Found empty file: %s\n", input_filename_.c_str());
|
||||
return false;
|
||||
}
|
||||
number_of_frames_ = source_file_size / frame_length_in_bytes_;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileHandlerImpl::Close() {
|
||||
if (input_file_ != NULL) {
|
||||
fclose(input_file_);
|
||||
input_file_ = NULL;
|
||||
}
|
||||
if (output_file_ != NULL) {
|
||||
fclose(output_file_);
|
||||
output_file_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileHandlerImpl::ReadFrame(WebRtc_UWord8* source_buffer) {
|
||||
assert(source_buffer);
|
||||
if (input_file_ == NULL) {
|
||||
fprintf(stderr, "FileHandler is not initialized (input file is NULL)\n");
|
||||
return false;
|
||||
}
|
||||
fread(source_buffer, 1, frame_length_in_bytes_, input_file_);
|
||||
if (feof(input_file_) != 0) {
|
||||
return false; // no more frames to process
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
WebRtc_UWord64 FileHandlerImpl::GetFileSize(std::string filename) {
|
||||
FILE* f = fopen(filename.c_str(), "rb");
|
||||
WebRtc_UWord64 size = 0;
|
||||
if (f != NULL) {
|
||||
if (fseek(f, 0, SEEK_END) == 0) {
|
||||
size = ftell(f);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
bool FileHandlerImpl::WriteFrame(WebRtc_UWord8* frame_buffer) {
|
||||
assert(frame_buffer);
|
||||
if (output_file_ == NULL) {
|
||||
fprintf(stderr, "FileHandler is not initialized (output file is NULL)\n");
|
||||
return false;
|
||||
}
|
||||
int bytes_written = fwrite(frame_buffer, 1, frame_length_in_bytes_,
|
||||
output_file_);
|
||||
if (bytes_written != frame_length_in_bytes_) {
|
||||
fprintf(stderr, "Failed to write %d bytes to file %s\n",
|
||||
frame_length_in_bytes_, output_filename_.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
82
src/modules/video_coding/codecs/test/file_handler.h
Normal file
82
src/modules/video_coding/codecs/test/file_handler.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 SRC_MODULES_VIDEO_CODING_CODECS_TEST_FILE_HANDLER_H_
|
||||
#define SRC_MODULES_VIDEO_CODING_CODECS_TEST_FILE_HANDLER_H_
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include "typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
// Handles reading and writing video files for the test framework's needs.
|
||||
class FileHandler {
|
||||
public:
|
||||
virtual ~FileHandler() {}
|
||||
|
||||
// Initializes the file handler, i.e. opens the input and output files etc.
|
||||
// This must be called before reading or writing frames has started.
|
||||
// Returns false if an error has occurred, in addition to printing to stderr.
|
||||
virtual bool Init() = 0;
|
||||
|
||||
// Reads a frame into the supplied buffer, which must contain enough space
|
||||
// for the frame size.
|
||||
// Returns true if there are more frames to read, false if we've already
|
||||
// read the last frame (in the previous call).
|
||||
virtual bool ReadFrame(WebRtc_UWord8* source_buffer) = 0;
|
||||
|
||||
// Writes a frame of the configured frame length to the output file.
|
||||
// Returns true if the write was successful, false otherwise.
|
||||
virtual bool WriteFrame(WebRtc_UWord8* frame_buffer) = 0;
|
||||
|
||||
// Closes the input and output files. Essentially makes this class impossible
|
||||
// to use anymore.
|
||||
virtual void Close() = 0;
|
||||
|
||||
// File size of the supplied file in bytes. Will return 0 if the file is
|
||||
// empty or if the file does not exist/is readable.
|
||||
virtual WebRtc_UWord64 GetFileSize(std::string filename) = 0;
|
||||
// Frame length in bytes of a single frame image.
|
||||
virtual int GetFrameLength() = 0;
|
||||
// Total number of frames in the input video source.
|
||||
virtual int GetNumberOfFrames() = 0;
|
||||
};
|
||||
|
||||
class FileHandlerImpl : public FileHandler {
|
||||
public:
|
||||
// Creates a file handler. The input file is assumed to exist and be readable
|
||||
// and the output file must be writable.
|
||||
FileHandlerImpl(std::string input_filename,
|
||||
std::string output_filename,
|
||||
int frame_length_in_bytes);
|
||||
virtual ~FileHandlerImpl();
|
||||
bool Init();
|
||||
bool ReadFrame(WebRtc_UWord8* source_buffer);
|
||||
bool WriteFrame(WebRtc_UWord8* frame_buffer);
|
||||
void Close();
|
||||
WebRtc_UWord64 GetFileSize(std::string filename);
|
||||
int GetFrameLength() { return frame_length_in_bytes_; }
|
||||
int GetNumberOfFrames() { return number_of_frames_; }
|
||||
|
||||
private:
|
||||
std::string input_filename_;
|
||||
std::string output_filename_;
|
||||
int frame_length_in_bytes_;
|
||||
int number_of_frames_;
|
||||
FILE* input_file_;
|
||||
FILE* output_file_;
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // SRC_MODULES_VIDEO_CODING_CODECS_TEST_FILE_HANDLER_H_
|
115
src/modules/video_coding/codecs/test/file_handler_unittest.cc
Normal file
115
src/modules/video_coding/codecs/test/file_handler_unittest.cc
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 "file_handler.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "unittest_utils.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
const std::string kInputFilename = "temp_inputfile.tmp";
|
||||
const std::string kOutputFilename = "temp_outputfile.tmp";
|
||||
const std::string kInputFileContents = "baz";
|
||||
const int kFrameLength = 1e5; // 100 kB
|
||||
|
||||
// Boilerplate code for proper unit tests for FileHandler.
|
||||
class FileHandlerTest: public testing::Test {
|
||||
protected:
|
||||
FileHandler* file_handler_;
|
||||
|
||||
FileHandlerTest() {
|
||||
// To avoid warnings when using ASSERT_DEATH
|
||||
::testing::FLAGS_gtest_death_test_style = "threadsafe";
|
||||
}
|
||||
|
||||
virtual ~FileHandlerTest() {
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
// Cleanup any existing files:
|
||||
std::remove(kInputFilename.c_str());
|
||||
std::remove(kOutputFilename.c_str());
|
||||
|
||||
// Create a dummy input file:
|
||||
FILE* dummy = fopen(kInputFilename.c_str(), "wb");
|
||||
fprintf(dummy, "%s", kInputFileContents.c_str());
|
||||
fclose(dummy);
|
||||
|
||||
file_handler_ = new FileHandlerImpl(kInputFilename, kOutputFilename,
|
||||
kFrameLength);
|
||||
ASSERT_TRUE(file_handler_->Init());
|
||||
}
|
||||
|
||||
void TearDown() {
|
||||
delete file_handler_;
|
||||
// Cleanup the temporary file:
|
||||
std::remove(kInputFilename.c_str());
|
||||
std::remove(kOutputFilename.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(FileHandlerTest, InitSuccess) {
|
||||
FileHandlerImpl file_handler(kInputFilename, kOutputFilename, kFrameLength);
|
||||
ASSERT_TRUE(file_handler.Init());
|
||||
ASSERT_EQ(kFrameLength, file_handler.GetFrameLength());
|
||||
ASSERT_EQ(0, file_handler.GetNumberOfFrames());
|
||||
}
|
||||
|
||||
TEST_F(FileHandlerTest, ReadFrame) {
|
||||
WebRtc_UWord8 buffer[3];
|
||||
bool result = file_handler_->ReadFrame(buffer);
|
||||
ASSERT_FALSE(result); // no more files to read
|
||||
ASSERT_EQ(kInputFileContents[0], buffer[0]);
|
||||
ASSERT_EQ(kInputFileContents[1], buffer[1]);
|
||||
ASSERT_EQ(kInputFileContents[2], buffer[2]);
|
||||
}
|
||||
|
||||
TEST_F(FileHandlerTest, ReadFrameUninitialized) {
|
||||
WebRtc_UWord8 buffer[3];
|
||||
FileHandlerImpl file_handler(kInputFilename, kOutputFilename, kFrameLength);
|
||||
ASSERT_FALSE(file_handler.ReadFrame(buffer));
|
||||
}
|
||||
|
||||
TEST_F(FileHandlerTest, ReadFrameNullArgument) {
|
||||
ASSERT_DEATH(file_handler_->ReadFrame(NULL), "");
|
||||
}
|
||||
|
||||
TEST_F(FileHandlerTest, WriteFrame) {
|
||||
WebRtc_UWord8 buffer[kFrameLength];
|
||||
memset(buffer, 9, kFrameLength); // Write lots of 9s to the buffer
|
||||
bool result = file_handler_->WriteFrame(buffer);
|
||||
ASSERT_TRUE(result); // success
|
||||
// Close the file and verify the size:
|
||||
file_handler_->Close();
|
||||
ASSERT_EQ(kFrameLength,
|
||||
static_cast<int>(file_handler_->GetFileSize(kOutputFilename)));
|
||||
}
|
||||
|
||||
TEST_F(FileHandlerTest, WriteFrameUninitialized) {
|
||||
WebRtc_UWord8 buffer[3];
|
||||
FileHandlerImpl file_handler(kInputFilename, kOutputFilename, kFrameLength);
|
||||
ASSERT_FALSE(file_handler.WriteFrame(buffer));
|
||||
}
|
||||
|
||||
TEST_F(FileHandlerTest, WriteFrameNullArgument) {
|
||||
ASSERT_DEATH(file_handler_->WriteFrame(NULL), "");
|
||||
}
|
||||
|
||||
TEST_F(FileHandlerTest, GetFileSizeExistingFile) {
|
||||
ASSERT_EQ(kInputFileContents.length(),
|
||||
file_handler_->GetFileSize(kInputFilename));
|
||||
}
|
||||
|
||||
TEST_F(FileHandlerTest, GetFileSizeNonExistingFile) {
|
||||
ASSERT_EQ(0u, file_handler_->GetFileSize("non-existing-file.tmp"));
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
104
src/modules/video_coding/codecs/test/mocks.h
Normal file
104
src/modules/video_coding/codecs/test/mocks.h
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 SRC_MODULES_VIDEO_CODING_CODECS_TEST_MOCKS_H_
|
||||
#define SRC_MODULES_VIDEO_CODING_CODECS_TEST_MOCKS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "file_handler.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "packet_manipulator.h"
|
||||
#include "typedefs.h"
|
||||
|
||||
// This file contains mocks that are used by the unit tests.
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class MockVideoEncoder : public VideoEncoder {
|
||||
public:
|
||||
MOCK_CONST_METHOD2(Version,
|
||||
WebRtc_Word32(WebRtc_Word8 *version, WebRtc_Word32 length));
|
||||
MOCK_METHOD3(InitEncode,
|
||||
WebRtc_Word32(const VideoCodec* codecSettings,
|
||||
WebRtc_Word32 numberOfCores,
|
||||
WebRtc_UWord32 maxPayloadSize));
|
||||
MOCK_METHOD3(Encode,
|
||||
WebRtc_Word32(const RawImage& inputImage,
|
||||
const CodecSpecificInfo* codecSpecificInfo,
|
||||
VideoFrameType frameType));
|
||||
MOCK_METHOD1(RegisterEncodeCompleteCallback,
|
||||
WebRtc_Word32(EncodedImageCallback* callback));
|
||||
MOCK_METHOD0(Release,
|
||||
WebRtc_Word32());
|
||||
MOCK_METHOD0(Reset,
|
||||
WebRtc_Word32());
|
||||
MOCK_METHOD1(SetPacketLoss,
|
||||
WebRtc_Word32(WebRtc_UWord32 packetLoss));
|
||||
MOCK_METHOD2(SetRates,
|
||||
WebRtc_Word32(WebRtc_UWord32 newBitRate, WebRtc_UWord32 frameRate));
|
||||
MOCK_METHOD1(SetPeriodicKeyFrames,
|
||||
WebRtc_Word32(bool enable));
|
||||
MOCK_METHOD2(CodecConfigParameters,
|
||||
WebRtc_Word32(WebRtc_UWord8* /*buffer*/, WebRtc_Word32));
|
||||
};
|
||||
|
||||
class MockVideoDecoder : public VideoDecoder {
|
||||
public:
|
||||
MOCK_METHOD2(InitDecode,
|
||||
WebRtc_Word32(const VideoCodec* codecSettings,
|
||||
WebRtc_Word32 numberOfCores));
|
||||
MOCK_METHOD5(Decode,
|
||||
WebRtc_Word32(const EncodedImage& inputImage,
|
||||
bool missingFrames,
|
||||
const RTPFragmentationHeader* fragmentation,
|
||||
const CodecSpecificInfo* codecSpecificInfo,
|
||||
WebRtc_Word64 renderTimeMs));
|
||||
MOCK_METHOD1(RegisterDecodeCompleteCallback,
|
||||
WebRtc_Word32(DecodedImageCallback* callback));
|
||||
MOCK_METHOD0(Release,
|
||||
WebRtc_Word32());
|
||||
MOCK_METHOD0(Reset,
|
||||
WebRtc_Word32());
|
||||
MOCK_METHOD2(SetCodecConfigParameters,
|
||||
WebRtc_Word32(const WebRtc_UWord8* /*buffer*/, WebRtc_Word32));
|
||||
MOCK_METHOD0(Copy,
|
||||
VideoDecoder*());
|
||||
};
|
||||
|
||||
namespace test {
|
||||
|
||||
class MockFileHandler : public FileHandler {
|
||||
public:
|
||||
MOCK_METHOD0(Init,
|
||||
bool());
|
||||
MOCK_METHOD1(ReadFrame,
|
||||
bool(WebRtc_UWord8* source_buffer));
|
||||
MOCK_METHOD1(WriteFrame,
|
||||
bool(WebRtc_UWord8* frame_buffer));
|
||||
MOCK_METHOD0(Close,
|
||||
void());
|
||||
MOCK_METHOD1(GetFileSize,
|
||||
WebRtc_UWord64(std::string filename));
|
||||
MOCK_METHOD0(GetFrameLength,
|
||||
int());
|
||||
MOCK_METHOD0(GetNumberOfFrames,
|
||||
int());
|
||||
};
|
||||
|
||||
class MockPacketManipulator : public PacketManipulator {
|
||||
public:
|
||||
MOCK_METHOD1(ManipulatePackets,
|
||||
int(webrtc::EncodedImage* encoded_image));
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // SRC_MODULES_VIDEO_CODING_CODECS_TEST_MOCKS_H_
|
77
src/modules/video_coding/codecs/test/packet_manipulator.cc
Normal file
77
src/modules/video_coding/codecs/test/packet_manipulator.cc
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 "packet_manipulator.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
PacketManipulatorImpl::PacketManipulatorImpl(PacketReader* packet_reader,
|
||||
const NetworkingConfig& config)
|
||||
: packet_reader_(packet_reader),
|
||||
config_(config),
|
||||
active_burst_packets_(0) {
|
||||
assert(packet_reader);
|
||||
}
|
||||
|
||||
PacketManipulatorImpl::~PacketManipulatorImpl() {
|
||||
}
|
||||
|
||||
int PacketManipulatorImpl::ManipulatePackets(
|
||||
webrtc::EncodedImage* encoded_image) {
|
||||
assert(encoded_image);
|
||||
int nbr_packets_dropped = 0;
|
||||
// There's no need to build a copy of the image data since viewing an
|
||||
// EncodedImage object, setting the length to a new lower value represents
|
||||
// that everything is dropped after that position in the byte array.
|
||||
// EncodedImage._size is the allocated bytes.
|
||||
// EncodedImage._length is how many that are filled with data.
|
||||
int new_length = 0;
|
||||
packet_reader_->InitializeReading(encoded_image->_buffer,
|
||||
encoded_image->_length,
|
||||
config_.packet_size_in_bytes);
|
||||
WebRtc_UWord8* packet = NULL;
|
||||
int nbr_bytes_to_read;
|
||||
// keep track of if we've lost any packets, since then we shall loose
|
||||
// the remains of the current frame:
|
||||
bool packet_loss_has_occurred = false;
|
||||
while ((nbr_bytes_to_read = packet_reader_->NextPacket(&packet)) > 0) {
|
||||
// Check if we're currently in a packet loss burst that is not completed:
|
||||
if (active_burst_packets_ > 0) {
|
||||
active_burst_packets_--;
|
||||
nbr_packets_dropped++;
|
||||
} else if (RandomUniform() < config_.packet_loss_probability ||
|
||||
packet_loss_has_occurred) {
|
||||
packet_loss_has_occurred = true;
|
||||
nbr_packets_dropped++;
|
||||
if (config_.packet_loss_mode == kBurst) {
|
||||
// Initiate a new burst
|
||||
active_burst_packets_ = config_.packet_loss_burst_length - 1;
|
||||
}
|
||||
} else {
|
||||
new_length += nbr_bytes_to_read;
|
||||
}
|
||||
}
|
||||
encoded_image->_length = new_length;
|
||||
if (nbr_packets_dropped > 0) {
|
||||
// Must set completeFrame to false to inform the decoder about this:
|
||||
encoded_image->_completeFrame = false;
|
||||
log("Dropped %d packets for frame %d (frame length: %d)\n",
|
||||
nbr_packets_dropped, encoded_image->_timeStamp,
|
||||
encoded_image->_length);
|
||||
}
|
||||
return nbr_packets_dropped;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtcc
|
106
src/modules/video_coding/codecs/test/packet_manipulator.h
Normal file
106
src/modules/video_coding/codecs/test/packet_manipulator.h
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 SRC_MODULES_VIDEO_CODING_CODECS_TEST_PACKET_MANIPULATOR_H_
|
||||
#define SRC_MODULES_VIDEO_CODING_CODECS_TEST_PACKET_MANIPULATOR_H_
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "packet_reader.h"
|
||||
#include "video_codec_interface.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
// Which mode the packet loss shall be performed according to.
|
||||
enum PacketLossMode {
|
||||
// Drops packets with a configured probability independently for each packet
|
||||
kUniform,
|
||||
// Drops packets similar to uniform but when a packet is being dropped,
|
||||
// the number of lost packets in a row is equal to the configured burst
|
||||
// length.
|
||||
kBurst
|
||||
};
|
||||
|
||||
// Contains configurations related to networking and simulation of
|
||||
// scenarios caused by network interference.
|
||||
struct NetworkingConfig {
|
||||
NetworkingConfig()
|
||||
: packet_size_in_bytes(1500), max_payload_size_in_bytes(1440),
|
||||
packet_loss_mode(kUniform), packet_loss_probability(0.0),
|
||||
packet_loss_burst_length(1) {
|
||||
}
|
||||
|
||||
// Packet size in bytes. Default: 1500 bytes.
|
||||
int packet_size_in_bytes;
|
||||
|
||||
// Encoder specific setting of maximum size in bytes of each payload.
|
||||
// Default: 1440 bytes.
|
||||
int max_payload_size_in_bytes;
|
||||
|
||||
// Packet loss mode. Two different packet loss models are supported:
|
||||
// uniform or burst. This setting has no effect unless
|
||||
// packet_loss_probability is >0.
|
||||
// Default: uniform.
|
||||
PacketLossMode packet_loss_mode;
|
||||
|
||||
// Packet loss probability. A value between 0.0 and 1.0 that defines the
|
||||
// probability of a packet being lost. 0.1 means 10% and so on.
|
||||
// Default: 0 (no loss).
|
||||
double packet_loss_probability;
|
||||
|
||||
// Packet loss burst length. Defines how many packets will be lost in a burst
|
||||
// when a packet has been decided to be lost. Must be >=1. Default: 1.
|
||||
int packet_loss_burst_length;
|
||||
};
|
||||
|
||||
// Class for simulating packet loss on the encoded frame data.
|
||||
// When a packet loss has occurred in a frame, the remaining data in that
|
||||
// frame is lost (even if burst length is only a single packet).
|
||||
// TODO(kjellander): Support discarding only individual packets in the frame
|
||||
// when CL 172001 has been submitted. This also requires a correct
|
||||
// fragmentation header to be passed to the decoder.
|
||||
//
|
||||
// To get a deterministic behavior of the packet dropping, initialize the
|
||||
// random generator with a fixed value before using this class, e.g. srand(0);
|
||||
class PacketManipulator {
|
||||
public:
|
||||
virtual ~PacketManipulator() {}
|
||||
|
||||
// Manipulates the data of the encoded_image to simulate parts being lost
|
||||
// during transport.
|
||||
// If packets are dropped from frame data, the completedFrame field will be
|
||||
// set to false.
|
||||
// Returns the number of packets being dropped.
|
||||
virtual int
|
||||
ManipulatePackets(webrtc::EncodedImage* encoded_image) = 0;
|
||||
};
|
||||
|
||||
class PacketManipulatorImpl : public PacketManipulator {
|
||||
public:
|
||||
PacketManipulatorImpl(PacketReader* packet_reader,
|
||||
const NetworkingConfig& config);
|
||||
virtual ~PacketManipulatorImpl();
|
||||
virtual int ManipulatePackets(webrtc::EncodedImage* encoded_image);
|
||||
|
||||
private:
|
||||
// Returns a uniformly distributed random value between 0.0 and 1.0
|
||||
inline double RandomUniform() {
|
||||
return (std::rand() + 1.0)/(RAND_MAX + 1.0);
|
||||
}
|
||||
PacketReader* packet_reader_;
|
||||
const NetworkingConfig& config_;
|
||||
// Used to simulate a burst over several frames.
|
||||
int active_burst_packets_;
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // SRC_MODULES_VIDEO_CODING_CODECS_TEST_PACKET_MANIPULATOR_H_
|
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 "gtest/gtest.h"
|
||||
#include "packet_manipulator.h"
|
||||
#include "typedefs.h"
|
||||
#include "unittest_utils.h"
|
||||
#include "video_codec_interface.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
class PacketManipulatorTest: public PacketRelatedTest {
|
||||
protected:
|
||||
PacketReader packet_reader_;
|
||||
EncodedImage image_;
|
||||
static const double kNeverDropProbability = 0.0;
|
||||
static const double kAlwaysDropProbability = 1.0;
|
||||
static const int kBurstLength = 1;
|
||||
NetworkingConfig drop_config_;
|
||||
NetworkingConfig no_drop_config_;
|
||||
|
||||
PacketManipulatorTest() {
|
||||
// To avoid warnings when using ASSERT_DEATH
|
||||
::testing::FLAGS_gtest_death_test_style = "threadsafe";
|
||||
|
||||
image_._buffer = packet_data_;
|
||||
image_._length = kPacketDataLength;
|
||||
image_._size = kPacketDataLength;
|
||||
|
||||
drop_config_.packet_size_in_bytes = kPacketSizeInBytes;
|
||||
drop_config_.packet_loss_probability = kAlwaysDropProbability;
|
||||
drop_config_.packet_loss_burst_length = kBurstLength;
|
||||
drop_config_.packet_loss_mode = kUniform;
|
||||
|
||||
no_drop_config_.packet_size_in_bytes = kPacketSizeInBytes;
|
||||
no_drop_config_.packet_loss_probability = kNeverDropProbability;
|
||||
no_drop_config_.packet_loss_burst_length = kBurstLength;
|
||||
no_drop_config_.packet_loss_mode = kUniform;
|
||||
}
|
||||
|
||||
virtual ~PacketManipulatorTest() {
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
PacketRelatedTest::SetUp();
|
||||
}
|
||||
|
||||
void TearDown() {
|
||||
PacketRelatedTest::TearDown();
|
||||
}
|
||||
|
||||
void VerifyPacketLoss(int expected_nbr_packets_dropped,
|
||||
int actual_nbr_packets_dropped,
|
||||
int expected_packet_data_length,
|
||||
WebRtc_UWord8* expected_packet_data,
|
||||
EncodedImage& actual_image) {
|
||||
EXPECT_EQ(expected_nbr_packets_dropped, actual_nbr_packets_dropped);
|
||||
EXPECT_EQ(expected_packet_data_length, static_cast<int>(image_._length));
|
||||
EXPECT_EQ(0, memcmp(expected_packet_data, actual_image._buffer,
|
||||
expected_packet_data_length));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(PacketManipulatorTest, Constructor) {
|
||||
PacketManipulatorImpl manipulator(&packet_reader_, no_drop_config_);
|
||||
}
|
||||
|
||||
TEST_F(PacketManipulatorTest, ConstructorNullArgument) {
|
||||
ASSERT_DEATH(PacketManipulatorImpl manipulator(NULL, no_drop_config_), "");
|
||||
}
|
||||
|
||||
TEST_F(PacketManipulatorTest, NullImageArgument) {
|
||||
PacketManipulatorImpl manipulator(&packet_reader_, no_drop_config_);
|
||||
ASSERT_DEATH(manipulator.ManipulatePackets(NULL), "");
|
||||
}
|
||||
|
||||
TEST_F(PacketManipulatorTest, DropNone) {
|
||||
PacketManipulatorImpl manipulator(&packet_reader_, no_drop_config_);
|
||||
int nbr_packets_dropped = manipulator.ManipulatePackets(&image_);
|
||||
VerifyPacketLoss(0, nbr_packets_dropped, kPacketDataLength,
|
||||
packet_data_, image_);
|
||||
}
|
||||
|
||||
TEST_F(PacketManipulatorTest, UniformDropNoneSmallFrame) {
|
||||
int data_length = 400; // smaller than the packet size
|
||||
image_._length = data_length;
|
||||
PacketManipulatorImpl manipulator(&packet_reader_, no_drop_config_);
|
||||
int nbr_packets_dropped = manipulator.ManipulatePackets(&image_);
|
||||
|
||||
VerifyPacketLoss(0, nbr_packets_dropped, data_length,
|
||||
packet_data_, image_);
|
||||
}
|
||||
|
||||
TEST_F(PacketManipulatorTest, UniformDropAll) {
|
||||
PacketManipulatorImpl manipulator(&packet_reader_, drop_config_);
|
||||
int nbr_packets_dropped = manipulator.ManipulatePackets(&image_);
|
||||
VerifyPacketLoss(kPacketDataNumberOfPackets, nbr_packets_dropped,
|
||||
0, packet_data_, image_);
|
||||
}
|
||||
|
||||
TEST_F(PacketManipulatorTest, UniformDropSinglePacket) {
|
||||
drop_config_.packet_loss_probability = 0.5;
|
||||
PacketManipulatorImpl manipulator(&packet_reader_, drop_config_);
|
||||
// Execute the test target method:
|
||||
int nbr_packets_dropped = manipulator.ManipulatePackets(&image_);
|
||||
|
||||
// The deterministic behavior (since we've set srand) will
|
||||
// make the packet manipulator to throw away the second packet.
|
||||
// The third packet is lost because when we have lost one, the remains shall
|
||||
// also be discarded.
|
||||
VerifyPacketLoss(2, nbr_packets_dropped, kPacketSizeInBytes, packet1_,
|
||||
image_);
|
||||
}
|
||||
|
||||
TEST_F(PacketManipulatorTest, BurstDropNinePackets) {
|
||||
// Create a longer packet data structure
|
||||
const int kDataLength = kPacketSizeInBytes * 10;
|
||||
WebRtc_UWord8 data[kDataLength];
|
||||
WebRtc_UWord8* data_pointer = data;
|
||||
// Fill with 0s, 1s and so on to be able to easily verify which were dropped:
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
memset(data_pointer + i * kPacketSizeInBytes, i, kPacketSizeInBytes);
|
||||
}
|
||||
// Overwrite the defaults from the test fixture:
|
||||
image_._buffer = data;
|
||||
image_._length = kDataLength;
|
||||
image_._size = kDataLength;
|
||||
|
||||
drop_config_.packet_loss_probability = 0.4;
|
||||
drop_config_.packet_loss_burst_length = 5;
|
||||
drop_config_.packet_loss_mode = kBurst;
|
||||
PacketManipulatorImpl manipulator(&packet_reader_, drop_config_);
|
||||
// Execute the test target method:
|
||||
int nbr_packets_dropped = manipulator.ManipulatePackets(&image_);
|
||||
|
||||
// Should discard every packet after the first one.
|
||||
VerifyPacketLoss(9, nbr_packets_dropped, kPacketSizeInBytes, data, image_);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
57
src/modules/video_coding/codecs/test/packet_reader.cc
Normal file
57
src/modules/video_coding/codecs/test/packet_reader.cc
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 "packet_reader.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
PacketReader::PacketReader()
|
||||
: initialized_(false) {
|
||||
}
|
||||
|
||||
PacketReader::~PacketReader() {
|
||||
}
|
||||
|
||||
void PacketReader::InitializeReading(WebRtc_UWord8* data,
|
||||
int data_length_in_bytes,
|
||||
int packet_size_in_bytes) {
|
||||
assert(data);
|
||||
assert(data_length_in_bytes >= 0);
|
||||
assert(packet_size_in_bytes > 0);
|
||||
data_ = data;
|
||||
data_length_ = data_length_in_bytes;
|
||||
packet_size_ = packet_size_in_bytes;
|
||||
currentIndex_ = 0;
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
int PacketReader::NextPacket(WebRtc_UWord8** packet_pointer) {
|
||||
if (!initialized_) {
|
||||
fprintf(stderr, "Attempting to use uninitialized PacketReader!\n");
|
||||
return -1;
|
||||
}
|
||||
*packet_pointer = data_ + currentIndex_;
|
||||
// Check if we're about to read the last packet:
|
||||
if (data_length_ - currentIndex_ <= packet_size_) {
|
||||
int size = data_length_ - currentIndex_;
|
||||
currentIndex_ = data_length_;
|
||||
assert(size >= 0);
|
||||
return size;
|
||||
}
|
||||
currentIndex_ += packet_size_;
|
||||
assert(packet_size_ >= 0);
|
||||
return packet_size_;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
52
src/modules/video_coding/codecs/test/packet_reader.h
Normal file
52
src/modules/video_coding/codecs/test/packet_reader.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 SRC_MODULES_VIDEO_CODING_CODECS_TEST_PACKET_READER_H_
|
||||
#define SRC_MODULES_VIDEO_CODING_CODECS_TEST_PACKET_READER_H_
|
||||
|
||||
#include "typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
// Reads chunks of data to simulate network packets from a byte array.
|
||||
class PacketReader {
|
||||
public:
|
||||
PacketReader();
|
||||
virtual ~PacketReader();
|
||||
|
||||
// Inizializes a new reading operation. Must be done before invoking the
|
||||
// NextPacket method.
|
||||
// * data_length_in_bytes is the length of the data byte array. Must be >= 0.
|
||||
// 0 length will result in no packets are read.
|
||||
// * packet_size_in_bytes is the number of bytes to read in each NextPacket
|
||||
// method call. Must be > 0
|
||||
virtual void InitializeReading(WebRtc_UWord8* data, int data_length_in_bytes,
|
||||
int packet_size_in_bytes);
|
||||
|
||||
// Moves the supplied pointer to the beginning of the next packet.
|
||||
// Returns:
|
||||
// * The size of the packet ready to read (lower than the packet size for
|
||||
// the last packet)
|
||||
// * 0 if there are no more packets to read
|
||||
// * -1 if InitializeReading has not been called (also prints to stderr).
|
||||
virtual int NextPacket(WebRtc_UWord8** packet_pointer);
|
||||
|
||||
private:
|
||||
WebRtc_UWord8* data_;
|
||||
int data_length_;
|
||||
int packet_size_;
|
||||
int currentIndex_;
|
||||
bool initialized_;
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // SRC_MODULES_VIDEO_CODING_CODECS_TEST_PACKET_READER_H_
|
148
src/modules/video_coding/codecs/test/packet_reader_unittest.cc
Normal file
148
src/modules/video_coding/codecs/test/packet_reader_unittest.cc
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 "gtest/gtest.h"
|
||||
#include "packet_reader.h"
|
||||
#include "typedefs.h"
|
||||
#include "unittest_utils.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
class PacketReaderTest: public PacketRelatedTest {
|
||||
protected:
|
||||
PacketReader* reader_;
|
||||
|
||||
PacketReaderTest() {
|
||||
// To avoid warnings when using ASSERT_DEATH
|
||||
::testing::FLAGS_gtest_death_test_style = "threadsafe";
|
||||
}
|
||||
|
||||
virtual ~PacketReaderTest() {
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
reader_ = new PacketReader();
|
||||
}
|
||||
|
||||
void TearDown() {
|
||||
delete reader_;
|
||||
}
|
||||
|
||||
void VerifyPacketData(int expected_length,
|
||||
int actual_length,
|
||||
WebRtc_UWord8* original_data_pointer,
|
||||
WebRtc_UWord8* new_data_pointer) {
|
||||
EXPECT_EQ(expected_length, actual_length);
|
||||
EXPECT_EQ(*original_data_pointer, *new_data_pointer);
|
||||
EXPECT_EQ(0, memcmp(original_data_pointer, new_data_pointer,
|
||||
actual_length));
|
||||
}
|
||||
};
|
||||
|
||||
// Test lack of initialization
|
||||
TEST_F(PacketReaderTest, Uninitialized) {
|
||||
WebRtc_UWord8* data_pointer = NULL;
|
||||
EXPECT_EQ(-1, reader_->NextPacket(&data_pointer));
|
||||
EXPECT_EQ(NULL, data_pointer);
|
||||
}
|
||||
|
||||
TEST_F(PacketReaderTest, InitializeNullDataArgument) {
|
||||
ASSERT_DEATH(reader_->InitializeReading(NULL, kPacketDataLength,
|
||||
kPacketSizeInBytes), "");
|
||||
}
|
||||
|
||||
TEST_F(PacketReaderTest, InitializeInvalidLengthArgument) {
|
||||
ASSERT_DEATH(reader_->InitializeReading(packet_data_, -1, kPacketSizeInBytes),
|
||||
"");
|
||||
}
|
||||
|
||||
TEST_F(PacketReaderTest, InitializeZeroLengthArgument) {
|
||||
reader_->InitializeReading(packet_data_, 0, kPacketSizeInBytes);
|
||||
ASSERT_EQ(0, reader_->NextPacket(&packet_data_pointer_));
|
||||
}
|
||||
|
||||
TEST_F(PacketReaderTest, InitializeInvalidPacketSizeArgument) {
|
||||
ASSERT_DEATH(reader_->InitializeReading(packet_data_, kPacketDataLength,
|
||||
0), "");
|
||||
}
|
||||
|
||||
// Test with something smaller than one packet
|
||||
TEST_F(PacketReaderTest, NormalSmallData) {
|
||||
const int kDataLengthInBytes = 1499;
|
||||
WebRtc_UWord8 data[kDataLengthInBytes];
|
||||
WebRtc_UWord8* data_pointer = data;
|
||||
memset(data, 1, kDataLengthInBytes);
|
||||
|
||||
reader_->InitializeReading(data, kDataLengthInBytes, kPacketSizeInBytes);
|
||||
int length_to_read = reader_->NextPacket(&data_pointer);
|
||||
VerifyPacketData(kDataLengthInBytes, length_to_read, data, data_pointer);
|
||||
EXPECT_EQ(0, data_pointer - data); // pointer hasn't moved
|
||||
|
||||
// Reading another one shall result in 0 bytes:
|
||||
length_to_read = reader_->NextPacket(&data_pointer);
|
||||
EXPECT_EQ(0, length_to_read);
|
||||
EXPECT_EQ(kDataLengthInBytes, data_pointer - data);
|
||||
}
|
||||
|
||||
// Test with data length that exactly matches one packet
|
||||
TEST_F(PacketReaderTest, NormalOnePacketData) {
|
||||
WebRtc_UWord8 data[kPacketSizeInBytes];
|
||||
WebRtc_UWord8* data_pointer = data;
|
||||
memset(data, 1, kPacketSizeInBytes);
|
||||
|
||||
reader_->InitializeReading(data, kPacketSizeInBytes, kPacketSizeInBytes);
|
||||
int length_to_read = reader_->NextPacket(&data_pointer);
|
||||
VerifyPacketData(kPacketSizeInBytes, length_to_read, data, data_pointer);
|
||||
EXPECT_EQ(0, data_pointer - data); // pointer hasn't moved
|
||||
|
||||
// Reading another one shall result in 0 bytes:
|
||||
length_to_read = reader_->NextPacket(&data_pointer);
|
||||
EXPECT_EQ(0, length_to_read);
|
||||
EXPECT_EQ(kPacketSizeInBytes, data_pointer - data);
|
||||
}
|
||||
|
||||
// Test with data length that will result in 3 packets
|
||||
TEST_F(PacketReaderTest, NormalLargeData) {
|
||||
reader_->InitializeReading(packet_data_, kPacketDataLength,
|
||||
kPacketSizeInBytes);
|
||||
|
||||
int length_to_read = reader_->NextPacket(&packet_data_pointer_);
|
||||
VerifyPacketData(kPacketSizeInBytes, length_to_read,
|
||||
packet1_, packet_data_pointer_);
|
||||
|
||||
length_to_read = reader_->NextPacket(&packet_data_pointer_);
|
||||
VerifyPacketData(kPacketSizeInBytes, length_to_read,
|
||||
packet2_, packet_data_pointer_);
|
||||
|
||||
length_to_read = reader_->NextPacket(&packet_data_pointer_);
|
||||
VerifyPacketData(1u, length_to_read,
|
||||
packet3_, packet_data_pointer_);
|
||||
|
||||
// Reading another one shall result in 0 bytes:
|
||||
length_to_read = reader_->NextPacket(&packet_data_pointer_);
|
||||
EXPECT_EQ(0, length_to_read);
|
||||
EXPECT_EQ(kPacketDataLength, packet_data_pointer_ - packet_data_);
|
||||
}
|
||||
|
||||
// Test with empty data.
|
||||
TEST_F(PacketReaderTest, EmptyData) {
|
||||
const int kDataLengthInBytes = 0;
|
||||
WebRtc_UWord8 data[kDataLengthInBytes];
|
||||
WebRtc_UWord8* data_pointer = data;
|
||||
reader_->InitializeReading(data, kDataLengthInBytes, kPacketSizeInBytes);
|
||||
EXPECT_EQ(kDataLengthInBytes, reader_->NextPacket(&data_pointer));
|
||||
EXPECT_EQ(*data, *data_pointer);
|
||||
// Do it again to make sure nothing changes
|
||||
EXPECT_EQ(kDataLengthInBytes, reader_->NextPacket(&data_pointer));
|
||||
EXPECT_EQ(*data, *data_pointer);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
17
src/modules/video_coding/codecs/test/run_all_unittests.cc
Normal file
17
src/modules/video_coding/codecs/test/run_all_unittests.cc
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 "gmock/gmock.h"
|
||||
#include "test/test_suite.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleMock(&argc, argv);
|
||||
webrtc::TestSuite test_suite(argc, argv);
|
||||
return test_suite.Run();
|
||||
}
|
176
src/modules/video_coding/codecs/test/stats.cc
Normal file
176
src/modules/video_coding/codecs/test/stats.cc
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 "stats.h"
|
||||
|
||||
#include <algorithm> // min_element, max_element
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
Stats::Stats() {
|
||||
}
|
||||
|
||||
Stats::~Stats() {
|
||||
}
|
||||
|
||||
bool LessForEncodeTime(const FrameStatistic& s1, const FrameStatistic& s2) {
|
||||
return s1.encode_time_in_us < s2.encode_time_in_us;
|
||||
}
|
||||
|
||||
bool LessForDecodeTime(const FrameStatistic& s1, const FrameStatistic& s2) {
|
||||
return s1.decode_time_in_us < s2.decode_time_in_us;
|
||||
}
|
||||
|
||||
bool LessForEncodedSize(const FrameStatistic& s1, const FrameStatistic& s2) {
|
||||
return s1.encoded_frame_length_in_bytes < s2.encoded_frame_length_in_bytes;
|
||||
}
|
||||
|
||||
bool LessForBitRate(const FrameStatistic& s1, const FrameStatistic& s2) {
|
||||
return s1.bit_rate_in_kbps < s2.bit_rate_in_kbps;
|
||||
}
|
||||
|
||||
|
||||
FrameStatistic& Stats::NewFrame(int frame_number) {
|
||||
assert(frame_number >= 0);
|
||||
FrameStatistic stat;
|
||||
stat.frame_number = frame_number;
|
||||
stats_.push_back(stat);
|
||||
return stats_[frame_number];
|
||||
}
|
||||
|
||||
void Stats::PrintSummary() {
|
||||
log("Processing summary:\n");
|
||||
if (stats_.size() == 0) {
|
||||
log("No frame statistics have been logged yet.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate min, max, average and total encoding time
|
||||
int total_encoding_time_in_us = 0;
|
||||
int total_decoding_time_in_us = 0;
|
||||
int total_encoded_frames_lengths = 0;
|
||||
int total_encoded_key_frames_lengths = 0;
|
||||
int total_encoded_nonkey_frames_lengths = 0;
|
||||
int nbr_keyframes = 0;
|
||||
int nbr_nonkeyframes = 0;
|
||||
|
||||
for (FrameStatisticsIterator it = stats_.begin();
|
||||
it != stats_.end(); ++it) {
|
||||
total_encoding_time_in_us += it->encode_time_in_us;
|
||||
total_decoding_time_in_us += it->decode_time_in_us;
|
||||
total_encoded_frames_lengths += it->encoded_frame_length_in_bytes;
|
||||
if (it->frame_type == webrtc::kKeyFrame) {
|
||||
total_encoded_key_frames_lengths += it->encoded_frame_length_in_bytes;
|
||||
nbr_keyframes++;
|
||||
} else {
|
||||
total_encoded_nonkey_frames_lengths += it->encoded_frame_length_in_bytes;
|
||||
nbr_nonkeyframes++;
|
||||
}
|
||||
}
|
||||
|
||||
FrameStatisticsIterator frame;
|
||||
|
||||
// ENCODING
|
||||
log("Encoding time:\n");
|
||||
frame = min_element(stats_.begin(),
|
||||
stats_.end(), LessForEncodeTime);
|
||||
log(" Min : %7d us (frame %d)\n",
|
||||
frame->encode_time_in_us, frame->frame_number);
|
||||
|
||||
frame = max_element(stats_.begin(),
|
||||
stats_.end(), LessForEncodeTime);
|
||||
log(" Max : %7d us (frame %d)\n",
|
||||
frame->encode_time_in_us, frame->frame_number);
|
||||
|
||||
log(" Average : %7d us\n",
|
||||
total_encoding_time_in_us / stats_.size());
|
||||
|
||||
// DECODING
|
||||
log("Decoding time:\n");
|
||||
// only consider frames that were successfully decoded (packet loss may cause
|
||||
// failures)
|
||||
std::vector<FrameStatistic> decoded_frames;
|
||||
for (std::vector<FrameStatistic>::iterator it = stats_.begin();
|
||||
it != stats_.end(); ++it) {
|
||||
if (it->decoding_successful) {
|
||||
decoded_frames.push_back(*it);
|
||||
}
|
||||
}
|
||||
if (decoded_frames.size() == 0) {
|
||||
printf("No successfully decoded frames exist in this statistics.");
|
||||
} else {
|
||||
frame = min_element(decoded_frames.begin(),
|
||||
decoded_frames.end(), LessForDecodeTime);
|
||||
log(" Min : %7d us (frame %d)\n",
|
||||
frame->decode_time_in_us, frame->frame_number);
|
||||
|
||||
frame = max_element(decoded_frames.begin(),
|
||||
decoded_frames.end(), LessForDecodeTime);
|
||||
log(" Max : %7d us (frame %d)\n",
|
||||
frame->decode_time_in_us, frame->frame_number);
|
||||
|
||||
log(" Average : %7d us\n",
|
||||
total_decoding_time_in_us / decoded_frames.size());
|
||||
log(" Failures: %d frames failed to decode.\n",
|
||||
(stats_.size() - decoded_frames.size()));
|
||||
}
|
||||
|
||||
// SIZE
|
||||
log("Frame sizes:\n");
|
||||
frame = min_element(stats_.begin(),
|
||||
stats_.end(), LessForEncodedSize);
|
||||
log(" Min : %7d bytes (frame %d)\n",
|
||||
frame->encoded_frame_length_in_bytes, frame->frame_number);
|
||||
|
||||
frame = max_element(stats_.begin(),
|
||||
stats_.end(), LessForEncodedSize);
|
||||
log(" Max : %7d bytes (frame %d)\n",
|
||||
frame->encoded_frame_length_in_bytes, frame->frame_number);
|
||||
|
||||
log(" Average : %7d bytes\n",
|
||||
total_encoded_frames_lengths / stats_.size());
|
||||
if (nbr_keyframes > 0) {
|
||||
log(" Average key frame size : %7d bytes (%d keyframes)\n",
|
||||
total_encoded_key_frames_lengths / nbr_keyframes,
|
||||
nbr_keyframes);
|
||||
}
|
||||
if (nbr_nonkeyframes > 0) {
|
||||
log(" Average non-key frame size: %7d bytes (%d frames)\n",
|
||||
total_encoded_nonkey_frames_lengths / nbr_nonkeyframes,
|
||||
nbr_nonkeyframes);
|
||||
}
|
||||
|
||||
// BIT RATE
|
||||
log("Bit rates:\n");
|
||||
frame = min_element(stats_.begin(),
|
||||
stats_.end(), LessForBitRate);
|
||||
log(" Min bit rate: %7d kbps (frame %d)\n",
|
||||
frame->bit_rate_in_kbps, frame->frame_number);
|
||||
|
||||
frame = max_element(stats_.begin(),
|
||||
stats_.end(), LessForBitRate);
|
||||
log(" Max bit rate: %7d kbps (frame %d)\n",
|
||||
frame->bit_rate_in_kbps, frame->frame_number);
|
||||
|
||||
log("\n");
|
||||
log("Total encoding time : %7d ms.\n",
|
||||
total_encoding_time_in_us / 1000);
|
||||
log("Total decoding time : %7d ms.\n",
|
||||
total_decoding_time_in_us / 1000);
|
||||
log("Total processing time: %7d ms.\n",
|
||||
(total_encoding_time_in_us + total_decoding_time_in_us) / 1000);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
74
src/modules/video_coding/codecs/test/stats.h
Normal file
74
src/modules/video_coding/codecs/test/stats.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 SRC_MODULES_VIDEO_CODING_CODECS_TEST_STATS_H_
|
||||
#define SRC_MODULES_VIDEO_CODING_CODECS_TEST_STATS_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "video_image.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
// Contains statistics of a single frame that has been processed.
|
||||
struct FrameStatistic {
|
||||
FrameStatistic() :
|
||||
encoding_successful(false), decoding_successful(false),
|
||||
encode_return_code(0), decode_return_code(0),
|
||||
encode_time_in_us(0), decode_time_in_us(0),
|
||||
frame_number(0), packets_dropped(0), total_packets(0),
|
||||
bit_rate_in_kbps(0), encoded_frame_length_in_bytes(0) {
|
||||
};
|
||||
bool encoding_successful;
|
||||
bool decoding_successful;
|
||||
int encode_return_code;
|
||||
int decode_return_code;
|
||||
int encode_time_in_us;
|
||||
int decode_time_in_us;
|
||||
int frame_number;
|
||||
// How many packets were discarded of the encoded frame data (if any)
|
||||
int packets_dropped;
|
||||
int total_packets;
|
||||
|
||||
// Current bit rate. Calculated out of the size divided with the time
|
||||
// interval per frame.
|
||||
int bit_rate_in_kbps;
|
||||
|
||||
// Copied from EncodedImage
|
||||
int encoded_frame_length_in_bytes;
|
||||
webrtc::VideoFrameType frame_type;
|
||||
};
|
||||
|
||||
// Handles statistics from a single video processing run.
|
||||
// Contains calculation methods for interesting metrics from these stats.
|
||||
class Stats {
|
||||
public:
|
||||
typedef std::vector<FrameStatistic>::iterator FrameStatisticsIterator;
|
||||
|
||||
Stats();
|
||||
virtual ~Stats();
|
||||
|
||||
// Add a new statistic data object.
|
||||
// The frame number must be incrementing and start at zero in order to use
|
||||
// it as an index for the frame_statistics_ vector.
|
||||
// Returns the newly created statistic object.
|
||||
FrameStatistic& NewFrame(int frame_number);
|
||||
|
||||
// Prints a summary of all the statistics that have been gathered during the
|
||||
// processing
|
||||
void PrintSummary();
|
||||
|
||||
std::vector<FrameStatistic> stats_;
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // SRC_MODULES_VIDEO_CODING_CODECS_TEST_STATS_H_
|
62
src/modules/video_coding/codecs/test/stats_unittest.cc
Normal file
62
src/modules/video_coding/codecs/test/stats_unittest.cc
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 "gtest/gtest.h"
|
||||
#include "stats.h"
|
||||
#include "typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
class StatsTest: public testing::Test {
|
||||
protected:
|
||||
StatsTest() {
|
||||
}
|
||||
|
||||
virtual ~StatsTest() {
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
stats_ = new Stats();
|
||||
}
|
||||
|
||||
void TearDown() {
|
||||
delete stats_;
|
||||
}
|
||||
|
||||
Stats* stats_;
|
||||
};
|
||||
|
||||
// Test empty object
|
||||
TEST_F(StatsTest, Uninitialized) {
|
||||
EXPECT_EQ(0u, stats_->stats_.size());
|
||||
stats_->PrintSummary(); // should not crash
|
||||
}
|
||||
|
||||
// Add single frame stats and verify
|
||||
TEST_F(StatsTest, AddOne) {
|
||||
stats_->NewFrame(0u);
|
||||
FrameStatistic* frameStat = &stats_->stats_[0];
|
||||
EXPECT_EQ(0, frameStat->frame_number);
|
||||
}
|
||||
|
||||
// Add multiple frame stats and verify
|
||||
TEST_F(StatsTest, AddMany) {
|
||||
int nbr_of_frames = 1000;
|
||||
for (int i = 0; i < nbr_of_frames; ++i) {
|
||||
FrameStatistic& frameStat = stats_->NewFrame(i);
|
||||
EXPECT_EQ(i, frameStat.frame_number);
|
||||
}
|
||||
EXPECT_EQ(nbr_of_frames, static_cast<int>(stats_->stats_.size()));
|
||||
|
||||
stats_->PrintSummary(); // should not crash
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
58
src/modules/video_coding/codecs/test/unittest_utils.h
Normal file
58
src/modules/video_coding/codecs/test/unittest_utils.h
Normal file
@ -0,0 +1,58 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
// Author: kjellander@google.com (Henrik Kjellander)
|
||||
|
||||
|
||||
#ifndef SRC_MODULES_VIDEO_CODING_CODECS_TEST_UNITTEST_UTILS_H_
|
||||
#define SRC_MODULES_VIDEO_CODING_CODECS_TEST_UNITTEST_UTILS_H_
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
const int kPacketSizeInBytes = 1500;
|
||||
const int kPacketDataLength = kPacketSizeInBytes * 2 + 1;
|
||||
const int kPacketDataNumberOfPackets = 3;
|
||||
|
||||
// A base test fixture for packet related tests. Contains
|
||||
// two full prepared packets with 1s, 2s in their data and a third packet with
|
||||
// a single 3 in it (size=1).
|
||||
// A packet data structure is also available, that contains these three packets
|
||||
// in order.
|
||||
class PacketRelatedTest: public testing::Test {
|
||||
protected:
|
||||
// Tree packet byte arrays with data used for verification:
|
||||
WebRtc_UWord8 packet1_[kPacketSizeInBytes];
|
||||
WebRtc_UWord8 packet2_[kPacketSizeInBytes];
|
||||
WebRtc_UWord8 packet3_[1];
|
||||
// Construct a data structure containing these packets
|
||||
WebRtc_UWord8 packet_data_[kPacketDataLength];
|
||||
WebRtc_UWord8* packet_data_pointer_;
|
||||
|
||||
PacketRelatedTest() {
|
||||
packet_data_pointer_ = packet_data_;
|
||||
|
||||
memset(packet1_, 1, kPacketSizeInBytes);
|
||||
memset(packet2_, 2, kPacketSizeInBytes);
|
||||
memset(packet3_, 3, 1);
|
||||
// Fill the packet_data:
|
||||
memcpy(packet_data_pointer_, packet1_, kPacketSizeInBytes);
|
||||
memcpy(packet_data_pointer_ + kPacketSizeInBytes, packet2_,
|
||||
kPacketSizeInBytes);
|
||||
memcpy(packet_data_pointer_ + kPacketSizeInBytes * 2, packet3_, 1);
|
||||
}
|
||||
|
||||
virtual ~PacketRelatedTest() {
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
// Initialize the random generator with 0 to get determenistic behaviour
|
||||
srand(0);
|
||||
}
|
||||
|
||||
void TearDown() {
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // SRC_MODULES_VIDEO_CODING_CODECS_TEST_UNITTEST_UTILS_H_
|
31
src/modules/video_coding/codecs/test/util.cc
Normal file
31
src/modules/video_coding/codecs/test/util.cc
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 "util.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include "google/gflags.h"
|
||||
|
||||
DEFINE_bool(verbose, true, "Verbose mode. Prints a lot of debugging info. "
|
||||
"Suitable for tracking progress but not for capturing output. "
|
||||
"Default: enabled");
|
||||
|
||||
int log(const char *format, ...) {
|
||||
int result = 0;
|
||||
if (FLAGS_verbose) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
result = vprintf(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
17
src/modules/video_coding/codecs/test/util.h
Normal file
17
src/modules/video_coding/codecs/test/util.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 SRC_MODULES_VIDEO_CODING_CODECS_TEST_UTIL_H_
|
||||
#define SRC_MODULES_VIDEO_CODING_CODECS_TEST_UTIL_H_
|
||||
|
||||
// Custom log method that only prints if the verbose flag is given
|
||||
// Supports all the standard printf parameters and formatting (just forwarded)
|
||||
int log(const char *format, ...);
|
||||
|
||||
#endif // SRC_MODULES_VIDEO_CODING_CODECS_TEST_UTIL_H_
|
@ -0,0 +1,88 @@
|
||||
# Copyright (c) 2011 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.
|
||||
|
||||
{
|
||||
# Exclude the test target when building with chromium.
|
||||
'conditions': [
|
||||
['build_with_chromium==0', {
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'video_codecs_test_framework',
|
||||
'type': '<(library)',
|
||||
'dependencies': [
|
||||
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
|
||||
'<(webrtc_root)/common_video/common_video.gyp:webrtc_vplib',
|
||||
'<(webrtc_root)/../testing/gtest.gyp:gtest',
|
||||
'<(webrtc_root)/../third_party/google-gflags/google-gflags.gyp:google-gflags',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../interface',
|
||||
'<(webrtc_root)/common_video/interface',
|
||||
'<(webrtc_root)/../testing/gtest/include',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'../interface',
|
||||
'<(webrtc_root)/../testing/gtest/include',
|
||||
],
|
||||
},
|
||||
'sources': [
|
||||
# header files
|
||||
'file_handler.h',
|
||||
'packet_manipulator.h',
|
||||
'packet_reader.h',
|
||||
'stats.h',
|
||||
'videoprocessor.h',
|
||||
'util.h',
|
||||
|
||||
# source files
|
||||
'file_handler.cc',
|
||||
'packet_manipulator.cc',
|
||||
'packet_reader.cc',
|
||||
'stats.cc',
|
||||
'videoprocessor.cc',
|
||||
'util.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'video_codecs_test_framework_unittests',
|
||||
'type': 'executable',
|
||||
'dependencies': [
|
||||
'video_codecs_test_framework',
|
||||
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
|
||||
'<(webrtc_root)/common_video/common_video.gyp:webrtc_vplib',
|
||||
'<(webrtc_root)/../testing/gmock.gyp:gmock',
|
||||
'<(webrtc_root)/../test/test.gyp:test_support',
|
||||
],
|
||||
'include_dirs': [
|
||||
'<(webrtc_root)/common_video/interface',
|
||||
],
|
||||
'sources': [
|
||||
# header files
|
||||
'mocks.h',
|
||||
|
||||
# source files
|
||||
'file_handler_unittest.cc',
|
||||
'packet_manipulator_unittest.cc',
|
||||
'packet_reader_unittest.cc',
|
||||
# cannot use the global run all file until it supports gmock:
|
||||
'run_all_unittests.cc',
|
||||
'stats_unittest.cc',
|
||||
'videoprocessor_unittest.cc',
|
||||
],
|
||||
},
|
||||
], # targets
|
||||
}], # build_with_chromium
|
||||
], # conditions
|
||||
}
|
||||
|
||||
# Local Variables:
|
||||
# tab-width:2
|
||||
# indent-tabs-mode:nil
|
||||
# End:
|
||||
# vim: set expandtab tabstop=2 shiftwidth=2:
|
243
src/modules/video_coding/codecs/test/videoprocessor.cc
Normal file
243
src/modules/video_coding/codecs/test/videoprocessor.cc
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 "videoprocessor.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
#include "cpu_wrapper.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
VideoProcessorImpl::VideoProcessorImpl(webrtc::VideoEncoder* encoder,
|
||||
webrtc::VideoDecoder* decoder,
|
||||
FileHandler* file_handler,
|
||||
PacketManipulator* packet_manipulator,
|
||||
const TestConfig& config,
|
||||
Stats* stats)
|
||||
: encoder_(encoder),
|
||||
decoder_(decoder),
|
||||
file_handler_(file_handler),
|
||||
packet_manipulator_(packet_manipulator),
|
||||
config_(config),
|
||||
stats_(stats),
|
||||
encode_callback_(NULL),
|
||||
decode_callback_(NULL),
|
||||
source_buffer_(NULL),
|
||||
first_key_frame_has_been_excluded_(false),
|
||||
last_frame_missing_(false),
|
||||
initialized_(false) {
|
||||
assert(encoder);
|
||||
assert(decoder);
|
||||
assert(file_handler);
|
||||
assert(packet_manipulator);
|
||||
assert(stats);
|
||||
}
|
||||
|
||||
bool VideoProcessorImpl::Init() {
|
||||
// Calculate a factor used for bit rate calculations:
|
||||
bit_rate_factor_ = config_.codec_settings.maxFramerate * 0.001 * 8; // bits
|
||||
|
||||
int frame_length_in_bytes = file_handler_->GetFrameLength();
|
||||
|
||||
// Initialize data structures used by the encoder/decoder APIs
|
||||
source_buffer_ = new WebRtc_UWord8[frame_length_in_bytes];
|
||||
last_successful_frame_buffer_ = new WebRtc_UWord8[frame_length_in_bytes];
|
||||
|
||||
// Set fixed properties common for all frames:
|
||||
source_frame_._width = config_.codec_settings.width;
|
||||
source_frame_._height = config_.codec_settings.height;
|
||||
source_frame_._length = frame_length_in_bytes;
|
||||
source_frame_._size = frame_length_in_bytes;
|
||||
|
||||
// Setup required callbacks for the encoder/decoder:
|
||||
encode_callback_ = new VideoProcessorEncodeCompleteCallback(this);
|
||||
decode_callback_ = new VideoProcessorDecodeCompleteCallback(this);
|
||||
WebRtc_Word32 register_result =
|
||||
encoder_->RegisterEncodeCompleteCallback(encode_callback_);
|
||||
if (register_result != WEBRTC_VIDEO_CODEC_OK) {
|
||||
fprintf(stderr, "Failed to register encode complete callback, return code: "
|
||||
"%d\n", register_result);
|
||||
return false;
|
||||
}
|
||||
register_result = decoder_->RegisterDecodeCompleteCallback(decode_callback_);
|
||||
if (register_result != WEBRTC_VIDEO_CODEC_OK) {
|
||||
fprintf(stderr, "Failed to register decode complete callback, return code: "
|
||||
"%d\n", register_result);
|
||||
return false;
|
||||
}
|
||||
// Init the encoder and decoder
|
||||
WebRtc_UWord32 nbr_of_cores = 1;
|
||||
if (!config_.use_single_core) {
|
||||
nbr_of_cores = CpuWrapper::DetectNumberOfCores();
|
||||
}
|
||||
WebRtc_Word32 init_result =
|
||||
encoder_->InitEncode(&config_.codec_settings, nbr_of_cores,
|
||||
config_.networking_config.max_payload_size_in_bytes);
|
||||
if (init_result != WEBRTC_VIDEO_CODEC_OK) {
|
||||
fprintf(stderr, "Failed to initialize VideoEncoder, return code: %d\n",
|
||||
init_result);
|
||||
return false;
|
||||
}
|
||||
init_result = decoder_->InitDecode(&config_.codec_settings, nbr_of_cores);
|
||||
if (init_result != WEBRTC_VIDEO_CODEC_OK) {
|
||||
fprintf(stderr, "Failed to initialize VideoDecoder, return code: %d\n",
|
||||
init_result);
|
||||
return false;
|
||||
}
|
||||
|
||||
log("Video Processor:\n");
|
||||
log(" #CPU cores used : %d\n", nbr_of_cores);
|
||||
log(" Total # of frames: %d\n", file_handler_->GetNumberOfFrames());
|
||||
log(" Codec settings:\n");
|
||||
log(" Start bitrate : %d kbps\n", config_.codec_settings.startBitrate);
|
||||
log(" Width : %d\n", config_.codec_settings.width);
|
||||
log(" Height : %d\n", config_.codec_settings.height);
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
VideoProcessorImpl::~VideoProcessorImpl() {
|
||||
delete[] source_buffer_;
|
||||
delete[] last_successful_frame_buffer_;
|
||||
encoder_->RegisterEncodeCompleteCallback(NULL);
|
||||
delete encode_callback_;
|
||||
decoder_->RegisterDecodeCompleteCallback(NULL);
|
||||
delete decode_callback_;
|
||||
}
|
||||
|
||||
bool VideoProcessorImpl::ProcessFrame(int frame_number) {
|
||||
assert(frame_number >=0);
|
||||
if (!initialized_) {
|
||||
fprintf(stderr, "Attempting to use uninitialized VideoProcessor!\n");
|
||||
return false;
|
||||
}
|
||||
if (file_handler_->ReadFrame(source_buffer_)) {
|
||||
// point the source frame buffer to the newly read frame data:
|
||||
source_frame_._buffer = source_buffer_;
|
||||
|
||||
// Ensure we have a new statistics data object we can fill:
|
||||
FrameStatistic& stat = stats_->NewFrame(frame_number);
|
||||
|
||||
encode_start_ = TickTime::Now();
|
||||
// Use the frame number as "timestamp" to identify frames
|
||||
source_frame_._timeStamp = frame_number;
|
||||
WebRtc_Word32 encode_result = encoder_->Encode(source_frame_);
|
||||
if (encode_result != WEBRTC_VIDEO_CODEC_OK) {
|
||||
fprintf(stderr, "Failed to encode frame %d, return code: %d\n",
|
||||
frame_number, encode_result);
|
||||
}
|
||||
stat.encode_return_code = encode_result;
|
||||
return true;
|
||||
} else {
|
||||
return false; // we've reached the last frame
|
||||
}
|
||||
}
|
||||
|
||||
void VideoProcessorImpl::FrameEncoded(EncodedImage* encoded_image) {
|
||||
TickTime encode_stop = TickTime::Now();
|
||||
int frame_number = encoded_image->_timeStamp;
|
||||
FrameStatistic& stat = stats_->stats_[frame_number];
|
||||
stat.encode_time_in_us = GetElapsedTimeMicroseconds(encode_start_,
|
||||
encode_stop);
|
||||
stat.encoding_successful = true;
|
||||
stat.encoded_frame_length_in_bytes = encoded_image->_length;
|
||||
stat.frame_number = encoded_image->_timeStamp;
|
||||
stat.frame_type = encoded_image->_frameType;
|
||||
stat.bit_rate_in_kbps = encoded_image->_length * bit_rate_factor_;
|
||||
stat.total_packets = encoded_image->_length /
|
||||
config_.networking_config.packet_size_in_bytes + 1;
|
||||
|
||||
// Perform packet loss if criteria is fullfilled:
|
||||
bool exclude_this_frame = false;
|
||||
// Only keyframes can be excluded
|
||||
if (encoded_image->_frameType == kKeyFrame) {
|
||||
switch (config_.exclude_frame_types) {
|
||||
case kExcludeOnlyFirstKeyFrame:
|
||||
if (!first_key_frame_has_been_excluded_) {
|
||||
first_key_frame_has_been_excluded_ = true;
|
||||
exclude_this_frame = true;
|
||||
}
|
||||
break;
|
||||
case kExcludeAllKeyFrames:
|
||||
exclude_this_frame = true;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
if (!exclude_this_frame) {
|
||||
stat.packets_dropped =
|
||||
packet_manipulator_->ManipulatePackets(encoded_image);
|
||||
}
|
||||
|
||||
// Keep track of if frames are lost due to packet loss so we can tell
|
||||
// this to the encoder (this is handled by the RTP logic in the full stack)
|
||||
decode_start_ = TickTime::Now();
|
||||
// TODO(kjellander): Pass fragmentation header to the decoder when
|
||||
// CL 172001 has been submitted and PacketManipulator supports this.
|
||||
WebRtc_Word32 decode_result = decoder_->Decode(*encoded_image,
|
||||
last_frame_missing_, NULL);
|
||||
stat.decode_return_code = decode_result;
|
||||
if (decode_result != WEBRTC_VIDEO_CODEC_OK) {
|
||||
// Write the last successful frame the output file to avoid getting it out
|
||||
// of sync with the source file for SSIM and PSNR comparisons:
|
||||
file_handler_->WriteFrame(last_successful_frame_buffer_);
|
||||
}
|
||||
// save status for losses so we can inform the decoder for the next frame:
|
||||
last_frame_missing_ = encoded_image->_length == 0;
|
||||
}
|
||||
|
||||
void VideoProcessorImpl::FrameDecoded(const RawImage& image) {
|
||||
TickTime decode_stop = TickTime::Now();
|
||||
int frame_number = image._timeStamp;
|
||||
// Report stats
|
||||
FrameStatistic& stat = stats_->stats_[frame_number];
|
||||
stat.decode_time_in_us = GetElapsedTimeMicroseconds(decode_start_,
|
||||
decode_stop);
|
||||
stat.decoding_successful = true;
|
||||
// Update our copy of the last successful frame:
|
||||
memcpy(last_successful_frame_buffer_, image._buffer, image._length);
|
||||
|
||||
bool write_success = file_handler_->WriteFrame(image._buffer);
|
||||
if (!write_success) {
|
||||
fprintf(stderr, "Failed to write frame %d to disk!", frame_number);
|
||||
}
|
||||
}
|
||||
|
||||
int VideoProcessorImpl::GetElapsedTimeMicroseconds(
|
||||
const webrtc::TickTime& start, const webrtc::TickTime& stop) {
|
||||
WebRtc_UWord64 encode_time = (stop - start).Microseconds();
|
||||
assert(encode_time <
|
||||
static_cast<unsigned int>(std::numeric_limits<int>::max()));
|
||||
return static_cast<int>(encode_time);
|
||||
}
|
||||
|
||||
// Callbacks
|
||||
WebRtc_Word32
|
||||
VideoProcessorImpl::VideoProcessorEncodeCompleteCallback::Encoded(
|
||||
EncodedImage& encoded_image,
|
||||
const webrtc::CodecSpecificInfo* codec_specific_info,
|
||||
const webrtc::RTPFragmentationHeader* fragmentation) {
|
||||
video_processor_->FrameEncoded(&encoded_image); // forward to parent class
|
||||
return 0;
|
||||
}
|
||||
WebRtc_Word32
|
||||
VideoProcessorImpl::VideoProcessorDecodeCompleteCallback::Decoded(
|
||||
RawImage& image) {
|
||||
video_processor_->FrameDecoded(image); // forward to parent class
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
201
src/modules/video_coding/codecs/test/videoprocessor.h
Normal file
201
src/modules/video_coding/codecs/test/videoprocessor.h
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 SRC_MODULES_VIDEO_CODING_CODECS_TEST_VIDEOPROCESSOR_H_
|
||||
#define SRC_MODULES_VIDEO_CODING_CODECS_TEST_VIDEOPROCESSOR_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "file_handler.h"
|
||||
#include "packet_manipulator.h"
|
||||
#include "stats.h"
|
||||
#include "tick_util.h"
|
||||
#include "video_codec_interface.h"
|
||||
|
||||
namespace webrtc {
|
||||
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
|
||||
};
|
||||
|
||||
// Test configuration for a test run
|
||||
struct TestConfig {
|
||||
TestConfig()
|
||||
: name(""), description(""), test_number(0),
|
||||
input_filename(""), output_filename(""), output_dir("out"),
|
||||
networking_config(), exclude_frame_types(kExcludeOnlyFirstKeyFrame),
|
||||
use_single_core(false) {
|
||||
};
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// The codec settings to use for the test (target bitrate, video size,
|
||||
// framerate and so on)
|
||||
webrtc::VideoCodec codec_settings;
|
||||
};
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
class VideoProcessorImpl : public VideoProcessor {
|
||||
public:
|
||||
VideoProcessorImpl(webrtc::VideoEncoder* encoder,
|
||||
webrtc::VideoDecoder* decoder,
|
||||
FileHandler* file_handler,
|
||||
PacketManipulator* packet_manipulator,
|
||||
const TestConfig& config,
|
||||
Stats* stats);
|
||||
virtual ~VideoProcessorImpl();
|
||||
virtual bool Init();
|
||||
virtual bool ProcessFrame(int frame_number);
|
||||
|
||||
private:
|
||||
// Invoked by the callback when a frame has completed encoding.
|
||||
void FrameEncoded(EncodedImage* encodedImage);
|
||||
// Invoked by the callback when a frame has completed decoding.
|
||||
void FrameDecoded(const RawImage& image);
|
||||
// Used for getting a 32-bit integer representing time
|
||||
// (checks the size is within signed 32-bit bounds before casting it)
|
||||
int GetElapsedTimeMicroseconds(const webrtc::TickTime& start,
|
||||
const webrtc::TickTime& stop);
|
||||
|
||||
webrtc::VideoEncoder* encoder_;
|
||||
webrtc::VideoDecoder* decoder_;
|
||||
FileHandler* file_handler_;
|
||||
PacketManipulator* packet_manipulator_;
|
||||
const TestConfig& config_;
|
||||
Stats* stats_;
|
||||
|
||||
EncodedImageCallback* encode_callback_;
|
||||
DecodedImageCallback* decode_callback_;
|
||||
// Buffer used for reading the source video file:
|
||||
WebRtc_UWord8* source_buffer_;
|
||||
// Keep track of the last successful frame, since we need to write that
|
||||
// when decoding fails:
|
||||
WebRtc_UWord8* last_successful_frame_buffer_;
|
||||
webrtc::RawImage source_frame_;
|
||||
// To keep track of if we have excluded the first key frame from packet loss:
|
||||
bool first_key_frame_has_been_excluded_;
|
||||
// To tell the decoder previous frame have been dropped due to packet loss:
|
||||
bool last_frame_missing_;
|
||||
// If Init() has executed successfully.
|
||||
bool initialized_;
|
||||
|
||||
// Statistics
|
||||
double bit_rate_factor_; // multiply frame length with this to get bit rate
|
||||
webrtc::TickTime encode_start_;
|
||||
webrtc::TickTime decode_start_;
|
||||
|
||||
// Callback class required to implement according to the VideoEncoder API.
|
||||
class VideoProcessorEncodeCompleteCallback
|
||||
: public webrtc::EncodedImageCallback {
|
||||
public:
|
||||
explicit VideoProcessorEncodeCompleteCallback(VideoProcessorImpl* vp)
|
||||
: video_processor_(vp) {
|
||||
}
|
||||
WebRtc_Word32 Encoded(
|
||||
webrtc::EncodedImage& encoded_image,
|
||||
const webrtc::CodecSpecificInfo* codec_specific_info = NULL,
|
||||
const webrtc::RTPFragmentationHeader* fragmentation = NULL);
|
||||
|
||||
private:
|
||||
VideoProcessorImpl* 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) {
|
||||
}
|
||||
WebRtc_Word32 Decoded(webrtc::RawImage& image);
|
||||
|
||||
private:
|
||||
VideoProcessorImpl* video_processor_;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // SRC_MODULES_VIDEO_CODING_CODECS_TEST_VIDEOPROCESSOR_H_
|
149
src/modules/video_coding/codecs/test/videoprocessor_unittest.cc
Normal file
149
src/modules/video_coding/codecs/test/videoprocessor_unittest.cc
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 "gtest/gtest.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "mocks.h"
|
||||
#include "packet_reader.h"
|
||||
#include "packet_manipulator.h"
|
||||
#include "typedefs.h"
|
||||
#include "unittest_utils.h"
|
||||
#include "videoprocessor.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::AtLeast;
|
||||
using ::testing::Return;
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
// Very basic testing for VideoProcessor. It's mostly tested by running the
|
||||
// video_quality_measurement program.
|
||||
class VideoProcessorTest: public testing::Test {
|
||||
protected:
|
||||
MockVideoEncoder encoder_mock_;
|
||||
MockVideoDecoder decoder_mock_;
|
||||
MockFileHandler file_handler_mock_;
|
||||
MockPacketManipulator packet_manipulator_mock_;
|
||||
Stats stats_;
|
||||
TestConfig config_;
|
||||
|
||||
VideoProcessorTest() {
|
||||
// To avoid warnings when using ASSERT_DEATH
|
||||
::testing::FLAGS_gtest_death_test_style = "threadsafe";
|
||||
}
|
||||
|
||||
virtual ~VideoProcessorTest() {
|
||||
}
|
||||
|
||||
void SetUp() {
|
||||
}
|
||||
|
||||
void TearDown() {
|
||||
}
|
||||
|
||||
void ExpectInit() {
|
||||
EXPECT_CALL(encoder_mock_, InitEncode(_, _, _))
|
||||
.Times(1);
|
||||
EXPECT_CALL(encoder_mock_, RegisterEncodeCompleteCallback(_))
|
||||
.Times(AtLeast(1));
|
||||
EXPECT_CALL(decoder_mock_, InitDecode(_, _))
|
||||
.Times(1);
|
||||
EXPECT_CALL(decoder_mock_, RegisterDecodeCompleteCallback(_))
|
||||
.Times(AtLeast(1));
|
||||
EXPECT_CALL(file_handler_mock_, GetNumberOfFrames())
|
||||
.WillOnce(Return(1));
|
||||
EXPECT_CALL(file_handler_mock_, GetFrameLength())
|
||||
.WillOnce(Return(150000));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(VideoProcessorTest, ConstructorNullEncoder) {
|
||||
ASSERT_DEATH(VideoProcessorImpl video_processor(NULL,
|
||||
&decoder_mock_,
|
||||
&file_handler_mock_,
|
||||
&packet_manipulator_mock_,
|
||||
config_,
|
||||
&stats_), "");
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessorTest, ConstructorNullDecoder) {
|
||||
ASSERT_DEATH(VideoProcessorImpl video_processor(&encoder_mock_,
|
||||
NULL,
|
||||
&file_handler_mock_,
|
||||
&packet_manipulator_mock_,
|
||||
config_,
|
||||
&stats_), "");
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessorTest, ConstructorNullFileHandler) {
|
||||
ASSERT_DEATH(VideoProcessorImpl video_processor(&encoder_mock_,
|
||||
&decoder_mock_,
|
||||
NULL,
|
||||
&packet_manipulator_mock_,
|
||||
config_,
|
||||
&stats_), "");
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessorTest, ConstructorNullPacketManipulator) {
|
||||
ASSERT_DEATH(VideoProcessorImpl video_processor(&encoder_mock_,
|
||||
&decoder_mock_,
|
||||
&file_handler_mock_,
|
||||
NULL,
|
||||
config_,
|
||||
&stats_), "");
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessorTest, ConstructorNullStats) {
|
||||
ASSERT_DEATH(VideoProcessorImpl video_processor(&encoder_mock_,
|
||||
&decoder_mock_,
|
||||
&file_handler_mock_,
|
||||
&packet_manipulator_mock_,
|
||||
config_,
|
||||
NULL), "");
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessorTest, Init) {
|
||||
ExpectInit();
|
||||
VideoProcessorImpl video_processor(&encoder_mock_, &decoder_mock_,
|
||||
&file_handler_mock_,
|
||||
&packet_manipulator_mock_, config_,
|
||||
&stats_);
|
||||
video_processor.Init();
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessorTest, ProcessFrame) {
|
||||
ExpectInit();
|
||||
EXPECT_CALL(encoder_mock_, Encode(_, _, _))
|
||||
.Times(1);
|
||||
EXPECT_CALL(file_handler_mock_, ReadFrame(_))
|
||||
.WillOnce(Return(true));
|
||||
// Since we don't return any callback from the mock, the decoder will not
|
||||
// be more than initialized...
|
||||
VideoProcessorImpl video_processor(&encoder_mock_, &decoder_mock_,
|
||||
&file_handler_mock_,
|
||||
&packet_manipulator_mock_, config_,
|
||||
&stats_);
|
||||
video_processor.Init();
|
||||
video_processor.ProcessFrame(0);
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessorTest, ProcessFrameInvalidArgument) {
|
||||
ExpectInit();
|
||||
VideoProcessorImpl video_processor(&encoder_mock_, &decoder_mock_,
|
||||
&file_handler_mock_,
|
||||
&packet_manipulator_mock_, config_,
|
||||
&stats_);
|
||||
video_processor.Init();
|
||||
ASSERT_DEATH(video_processor.ProcessFrame(-1), "");
|
||||
}
|
||||
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
@ -0,0 +1,46 @@
|
||||
# Copyright (c) 2011 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.
|
||||
|
||||
{
|
||||
# Exclude the test target when building with chromium.
|
||||
'conditions': [
|
||||
['build_with_chromium==0', {
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'video_quality_measurement',
|
||||
'type': 'executable',
|
||||
'dependencies': [
|
||||
'video_codecs_test_framework',
|
||||
'video_coding_test_lib',
|
||||
'webrtc_vp8',
|
||||
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
|
||||
'<(webrtc_root)/common_video/common_video.gyp:webrtc_vplib',
|
||||
'<(webrtc_root)/../third_party/google-gflags/google-gflags.gyp:google-gflags',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../test',
|
||||
'../interface',
|
||||
'<(webrtc_root)/common_video/interface',
|
||||
],
|
||||
'sources': [
|
||||
# header files
|
||||
|
||||
# source files
|
||||
'video_quality_measurement.cc',
|
||||
],
|
||||
},
|
||||
], # targets
|
||||
}], # build_with_chromium
|
||||
], # conditions
|
||||
}
|
||||
|
||||
# Local Variables:
|
||||
# tab-width:2
|
||||
# indent-tabs-mode:nil
|
||||
# End:
|
||||
# vim: set expandtab tabstop=2 shiftwidth=2:
|
@ -0,0 +1,337 @@
|
||||
/*
|
||||
* Copyright (c) 2011 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 <dirent.h> // for checking directory existence
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
|
||||
#include "google/gflags.h"
|
||||
#include "packet_manipulator.h"
|
||||
#include "packet_reader.h"
|
||||
#include "stats.h"
|
||||
#include "trace.h"
|
||||
#include "util.h"
|
||||
#include "video_metrics.h"
|
||||
#include "videoprocessor.h"
|
||||
#include "vp8.h"
|
||||
|
||||
DEFINE_string(test_name, "Quality test", "The name of the test to run. "
|
||||
"Default: Quality test.");
|
||||
DEFINE_string(test_description, "", "A more detailed description about what "
|
||||
"the current test is about.");
|
||||
DEFINE_string(input_filename, "", "Input file. "
|
||||
"The source video file to be encoded and decoded. Must be in "
|
||||
".yuv format");
|
||||
DEFINE_int32(width, -1, "Width in pixels of the frames in the input file.");
|
||||
DEFINE_int32(height, -1, "Height in pixels of the frames in the input file.");
|
||||
DEFINE_int32(framerate, 30, "Frame rate of the input file, in FPS "
|
||||
"(frames-per-second). ");
|
||||
DEFINE_string(output_dir, ".", "Output directory. "
|
||||
"The directory where the output file will be put. Must already "
|
||||
"exist.");
|
||||
DEFINE_bool(use_single_core, false, "Force using a single core. If set to "
|
||||
"true, only one core will be used 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 detected number of cores will be used. ");
|
||||
DEFINE_bool(disable_fixed_random_seed , false, "Set this flag to disable the"
|
||||
"usage of a fixed random seed for the random generator used "
|
||||
"for packet loss. Disabling this will cause consecutive runs "
|
||||
"loose packets at different locations, which is bad for "
|
||||
"reproducibility.");
|
||||
DEFINE_string(output_filename, "", "Output file. "
|
||||
"The name of the output video file resulting of the processing "
|
||||
"of the source file. By default this is the same name as the "
|
||||
"input file with '_out' appended before the extension.");
|
||||
DEFINE_int32(bitrate, 500, "Bit rate in kilobits/second.");
|
||||
DEFINE_int32(packet_size, 1500, "Simulated network packet size in bytes (MTU). "
|
||||
"Used for packet loss simulation.");
|
||||
DEFINE_int32(max_payload_size, 1440, "Max payload size in bytes for the "
|
||||
"encoder.");
|
||||
DEFINE_string(packet_loss_mode, "uniform", "Packet loss mode. Two different "
|
||||
"packet loss models are supported: uniform or burst. This "
|
||||
"setting has no effect unless packet_loss_rate is >0. ");
|
||||
DEFINE_double(packet_loss_probability, 0.0, "Packet loss probability. A value "
|
||||
"between 0.0 and 1.0 that defines the probability of a packet "
|
||||
"being lost. 0.1 means 10% and so on.");
|
||||
DEFINE_int32(packet_loss_burst_length, 1, "Packet loss burst length. Defines "
|
||||
"how many packets will be lost in a burst when a packet has been "
|
||||
"decided to be lost. Must be >=1.");
|
||||
DEFINE_bool(csv, false, "CSV output. Enabling this will output all frame "
|
||||
"statistics at the end of execution. Recommended to run combined "
|
||||
"with --noverbose to avoid mixing output.");
|
||||
|
||||
// Runs a quality measurement on the input file supplied to the program.
|
||||
// The input file must be in YUV format.
|
||||
int main(int argc, char* argv[]) {
|
||||
std::string program_name = argv[0];
|
||||
std::string usage = "Quality test application for video comparisons.\n"
|
||||
"Run " + program_name + " --helpshort for usage.\n"
|
||||
"Example usage:\n" + program_name +
|
||||
" --input_filename=filename.yuv --width=352 --height=288\n";
|
||||
google::SetUsageMessage(usage);
|
||||
|
||||
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
if (FLAGS_input_filename == "" || FLAGS_width == -1 || FLAGS_height == -1) {
|
||||
printf("%s\n", google::ProgramUsage());
|
||||
return 1;
|
||||
} else {
|
||||
webrtc::test::TestConfig config;
|
||||
config.name = FLAGS_test_name;
|
||||
config.description = FLAGS_test_description;
|
||||
|
||||
// Verify the input file exists and is readable:
|
||||
FILE* test_file;
|
||||
test_file = fopen(FLAGS_input_filename.c_str(), "rb");
|
||||
if (test_file == NULL) {
|
||||
fprintf(stderr, "Cannot read the specified input file: %s\n",
|
||||
FLAGS_input_filename.c_str());
|
||||
return 2;
|
||||
}
|
||||
fclose(test_file);
|
||||
config.input_filename = FLAGS_input_filename;
|
||||
|
||||
// Verify the output dir exists:
|
||||
DIR* output_dir = opendir(FLAGS_output_dir.c_str());
|
||||
if (output_dir == NULL) {
|
||||
fprintf(stderr, "Cannot find output directory: %s\n",
|
||||
FLAGS_output_dir.c_str());
|
||||
return 3;
|
||||
}
|
||||
closedir(output_dir);
|
||||
config.output_dir = FLAGS_output_dir;
|
||||
|
||||
// Manufacture an output filename if none was given:
|
||||
if (FLAGS_output_filename == "") {
|
||||
// Cut out the filename without extension from the given input file
|
||||
// (which may include a path)
|
||||
int startIndex = FLAGS_input_filename.find_last_of("/") + 1;
|
||||
if (startIndex == 0) {
|
||||
startIndex = 0;
|
||||
}
|
||||
FLAGS_output_filename =
|
||||
FLAGS_input_filename.substr(startIndex,
|
||||
FLAGS_input_filename.find_last_of(".")
|
||||
- startIndex) + "_out.yuv";
|
||||
}
|
||||
|
||||
// Verify output file can be written
|
||||
std::string output_filename;
|
||||
if (FLAGS_output_dir == ".") {
|
||||
output_filename = FLAGS_output_filename;
|
||||
} else {
|
||||
output_filename = FLAGS_output_dir + "/"+ FLAGS_output_filename;
|
||||
}
|
||||
test_file = fopen(output_filename.c_str(), "wb");
|
||||
if (test_file == NULL) {
|
||||
fprintf(stderr, "Cannot write output file: %s\n",
|
||||
output_filename.c_str());
|
||||
return 4;
|
||||
}
|
||||
fclose(test_file);
|
||||
config.output_filename = output_filename;
|
||||
|
||||
// Check single core flag
|
||||
config.use_single_core = FLAGS_use_single_core;
|
||||
|
||||
// Seed our random function if that flag is enabled. This will force
|
||||
// repeatable behaviour between runs
|
||||
if (!FLAGS_disable_fixed_random_seed) {
|
||||
srand(0);
|
||||
}
|
||||
|
||||
// Check the bit rate
|
||||
if (FLAGS_bitrate <= 0) {
|
||||
fprintf(stderr, "Bit rate must be >0 kbps, was: %d\n", FLAGS_bitrate);
|
||||
return 5;
|
||||
}
|
||||
config.codec_settings.startBitrate = FLAGS_bitrate;
|
||||
|
||||
// Check packet size and max payload size
|
||||
if (FLAGS_packet_size <= 0) {
|
||||
fprintf(stderr, "Packet size must be >0 bytes, was: %d\n",
|
||||
FLAGS_packet_size);
|
||||
return 6;
|
||||
}
|
||||
config.networking_config.packet_size_in_bytes = FLAGS_packet_size;
|
||||
|
||||
if (FLAGS_max_payload_size <= 0) {
|
||||
fprintf(stderr, "Max payload size must be >0 bytes, was: %d\n",
|
||||
FLAGS_max_payload_size);
|
||||
return 7;
|
||||
}
|
||||
config.networking_config.max_payload_size_in_bytes = FLAGS_max_payload_size;
|
||||
|
||||
// Check the width and height
|
||||
if (FLAGS_width <= 0 || FLAGS_height <= 0) {
|
||||
fprintf(stderr, "Width and height must be >0.");
|
||||
return 8;
|
||||
}
|
||||
config.codec_settings.width = FLAGS_width;
|
||||
config.codec_settings.height = FLAGS_height;
|
||||
|
||||
// Check framerate
|
||||
if (FLAGS_framerate <= 0) {
|
||||
fprintf(stderr, "Framerate be >0.");
|
||||
return 9;
|
||||
}
|
||||
config.codec_settings.maxFramerate = FLAGS_framerate;
|
||||
|
||||
// Check packet loss settings
|
||||
if (FLAGS_packet_loss_mode != "uniform" &&
|
||||
FLAGS_packet_loss_mode != "burst") {
|
||||
fprintf(stderr, "Unsupported packet loss mode, must be 'uniform' or "
|
||||
"'burst'\n.");
|
||||
return 10;
|
||||
}
|
||||
config.networking_config.packet_loss_mode = webrtc::test::kUniform;
|
||||
if (FLAGS_packet_loss_mode == "burst") {
|
||||
config.networking_config.packet_loss_mode = webrtc::test::kBurst;
|
||||
}
|
||||
|
||||
if (FLAGS_packet_loss_probability < 0.0 ||
|
||||
FLAGS_packet_loss_probability > 1.0) {
|
||||
fprintf(stderr, "Invalid packet loss probability. Must be 0.0 - 1.0, "
|
||||
"was: %f\n", FLAGS_packet_loss_probability);
|
||||
return 11;
|
||||
}
|
||||
config.networking_config.packet_loss_probability =
|
||||
FLAGS_packet_loss_probability;
|
||||
|
||||
if (FLAGS_packet_loss_burst_length < 1) {
|
||||
fprintf(stderr, "Invalid packet loss burst length, must be >=1, "
|
||||
"was: %d\n", FLAGS_packet_loss_burst_length);
|
||||
return 12;
|
||||
}
|
||||
config.networking_config.packet_loss_burst_length =
|
||||
FLAGS_packet_loss_burst_length;
|
||||
|
||||
// Calculate the size of each frame to read (according to YUV spec):
|
||||
int frame_length_in_bytes =
|
||||
3 * config.codec_settings.width * config.codec_settings.height / 2;
|
||||
|
||||
log("Quality test with parameters:\n");
|
||||
log(" Test name : %s\n", FLAGS_test_name.c_str());
|
||||
log(" Description : %s\n", FLAGS_test_description.c_str());
|
||||
log(" Input filename : %s\n", FLAGS_input_filename.c_str());
|
||||
log(" Output directory : %s\n", config.output_dir.c_str());
|
||||
log(" Output filename : %s\n", output_filename.c_str());
|
||||
log(" Frame size : %d bytes\n", frame_length_in_bytes);
|
||||
log(" Packet size : %d bytes\n", FLAGS_packet_size);
|
||||
log(" Max payload size : %d bytes\n", FLAGS_max_payload_size);
|
||||
log(" Packet loss:\n");
|
||||
log(" Mode : %s\n", FLAGS_packet_loss_mode.c_str());
|
||||
log(" Probability : %2.1f\n", FLAGS_packet_loss_probability);
|
||||
log(" Burst length : %d packets\n", FLAGS_packet_loss_burst_length);
|
||||
|
||||
webrtc::VP8Encoder encoder;
|
||||
webrtc::VP8Decoder decoder;
|
||||
webrtc::test::Stats stats;
|
||||
webrtc::test::FileHandlerImpl file_handler(config.input_filename,
|
||||
config.output_filename,
|
||||
frame_length_in_bytes);
|
||||
file_handler.Init();
|
||||
webrtc::test::PacketReader packet_reader;
|
||||
|
||||
webrtc::test::PacketManipulatorImpl packet_manipulator(
|
||||
&packet_reader, config.networking_config);
|
||||
webrtc::test::VideoProcessorImpl processor(&encoder, &decoder,
|
||||
&file_handler,
|
||||
&packet_manipulator,
|
||||
config, &stats);
|
||||
processor.Init();
|
||||
|
||||
int frame_number = 0;
|
||||
while (processor.ProcessFrame(frame_number)) {
|
||||
if (frame_number % 80 == 0) {
|
||||
log("\n"); // make the output a bit nicer.
|
||||
}
|
||||
log(".");
|
||||
frame_number++;
|
||||
}
|
||||
log("\n");
|
||||
log("Processed %d frames\n", frame_number);
|
||||
|
||||
// Release encoder and decoder to make sure they have finished processing:
|
||||
encoder.Release();
|
||||
decoder.Release();
|
||||
|
||||
// Verify statistics are correct:
|
||||
assert(frame_number == static_cast<int>(stats.stats_.size()));
|
||||
|
||||
// Close the files before we start using them for SSIM/PSNR calculations.
|
||||
file_handler.Close();
|
||||
|
||||
stats.PrintSummary();
|
||||
|
||||
// Calculate SSIM
|
||||
QualityMetricsResult ssimResult;
|
||||
log("Calculating SSIM...\n");
|
||||
SsimFromFiles(FLAGS_input_filename.c_str(), output_filename.c_str(),
|
||||
config.codec_settings.width,
|
||||
config.codec_settings.height, &ssimResult);
|
||||
log(" Average: %3.2f\n", ssimResult.average);
|
||||
log(" Min : %3.2f (frame %d)\n", ssimResult.min,
|
||||
ssimResult.min_frame_number);
|
||||
log(" Max : %3.2f (frame %d)\n", ssimResult.max,
|
||||
ssimResult.max_frame_number);
|
||||
|
||||
QualityMetricsResult psnrResult;
|
||||
log("Calculating PSNR...\n");
|
||||
PsnrFromFiles(FLAGS_input_filename.c_str(), output_filename.c_str(),
|
||||
config.codec_settings.width,
|
||||
config.codec_settings.height, &psnrResult);
|
||||
log(" Average: %3.2f\n", psnrResult.average);
|
||||
log(" Min : %3.2f (frame %d)\n", psnrResult.min,
|
||||
psnrResult.min_frame_number);
|
||||
log(" Max : %3.2f (frame %d)\n", psnrResult.max,
|
||||
psnrResult.max_frame_number);
|
||||
|
||||
if (FLAGS_csv) {
|
||||
log("\nCSV output (recommended to run with --noverbose to skip the "
|
||||
"above output)\n");
|
||||
printf("frame_number encoding_successful decoding_successful "
|
||||
"encode_return_code decode_return_code "
|
||||
"encode_time_in_us decode_time_in_us "
|
||||
"bit_rate_in_kbps encoded_frame_length_in_bytes frame_type "
|
||||
"packets_dropped total_packets "
|
||||
"ssim psnr\n");
|
||||
|
||||
for (unsigned int i = 0; i < stats.stats_.size(); ++i) {
|
||||
webrtc::test::FrameStatistic& f = stats.stats_[i];
|
||||
FrameResult& ssim = ssimResult.frames[i];
|
||||
FrameResult& psnr = psnrResult.frames[i];
|
||||
printf("%4d, %d, %d, %2d, %2d, %6d, %6d, %5d, %7d, %d, %2d, %2d, "
|
||||
"%5.3f, %5.2f\n",
|
||||
f.frame_number,
|
||||
f.encoding_successful,
|
||||
f.decoding_successful,
|
||||
f.encode_return_code,
|
||||
f.decode_return_code,
|
||||
f.encode_time_in_us,
|
||||
f.decode_time_in_us,
|
||||
f.bit_rate_in_kbps,
|
||||
f.encoded_frame_length_in_bytes,
|
||||
f.frame_type,
|
||||
f.packets_dropped,
|
||||
f.total_packets,
|
||||
ssim.value,
|
||||
psnr.value);
|
||||
}
|
||||
}
|
||||
log("Quality test finished!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user