Integration test that tracks dropped frames and compares video output.
The recorded frame timestamps are used to modify the output video on a frame-per-frame so it can be compared with the reference video using PSNR. This code will make it possible to use vie_auto_test for full stack comparisons with network interference and similar interesting simulations. There's some refactoring done in vie_comparison_test.cc to make it fit to the new test. Compiled and executed in Debug+Release on Linux, Mac and Windows. BUG= TEST=vie_auto_test --automated --gtest_filter=ViEVideoVerificationTest.* Review URL: http://webrtc-codereview.appspot.com/320002 git-svn-id: http://webrtc.googlecode.com/svn/trunk@1269 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
03532b5f41
commit
173b7bbc16
@ -1,136 +0,0 @@
|
||||
/*
|
||||
* 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 "gflags/gflags.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "testsupport/metrics/video_metrics.h"
|
||||
#include "vie_autotest.h"
|
||||
#include "vie_comparison_tests.h"
|
||||
#include "vie_integration_test_base.h"
|
||||
#include "vie_to_file_renderer.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// The input file must be QCIF since I420 gets scaled to that in the tests
|
||||
// (it is so bandwidth-heavy we have no choice). Our comparison algorithms
|
||||
// wouldn't like scaling, so this will work when we compare with the original.
|
||||
const std::string input_file = ViETest::GetResultOutputPath() +
|
||||
"resources/paris_qcif.yuv";
|
||||
const int input_width = 176;
|
||||
const int input_height = 144;
|
||||
|
||||
class ViEComparisonTest: public testing::Test {
|
||||
public:
|
||||
void SetUp() {
|
||||
std::string test_case_name =
|
||||
::testing::UnitTest::GetInstance()->current_test_info()->name();
|
||||
|
||||
std::string output_path = ViETest::GetResultOutputPath();
|
||||
std::string local_preview_filename =
|
||||
test_case_name + "-local-preview.yuv";
|
||||
std::string remote_filename =
|
||||
test_case_name + "-remote.yuv";
|
||||
|
||||
if (!local_file_renderer_.PrepareForRendering(output_path,
|
||||
local_preview_filename)) {
|
||||
FAIL() << "Could not open output file " << output_path <<
|
||||
local_preview_filename << " for writing.";
|
||||
}
|
||||
if (!remote_file_renderer_.PrepareForRendering(output_path,
|
||||
remote_filename)) {
|
||||
FAIL() << "Could not open output file " << output_path <<
|
||||
remote_filename << " for writing.";
|
||||
}
|
||||
}
|
||||
|
||||
void TearDown() {
|
||||
local_file_renderer_.StopRendering();
|
||||
remote_file_renderer_.StopRendering();
|
||||
|
||||
bool test_failed = ::testing::UnitTest::GetInstance()->
|
||||
current_test_info()->result()->Failed();
|
||||
|
||||
if (test_failed) {
|
||||
// Leave the files for analysis if the test failed
|
||||
local_file_renderer_.SaveOutputFile("failed-");
|
||||
remote_file_renderer_.SaveOutputFile("failed-");
|
||||
} else {
|
||||
// No reason to keep the files if we succeeded
|
||||
local_file_renderer_.DeleteOutputFile();
|
||||
remote_file_renderer_.DeleteOutputFile();
|
||||
}
|
||||
}
|
||||
protected:
|
||||
ViEToFileRenderer local_file_renderer_;
|
||||
ViEToFileRenderer remote_file_renderer_;
|
||||
|
||||
ViEComparisonTests tests_;
|
||||
};
|
||||
|
||||
TEST_F(ViEComparisonTest, RunsBaseStandardTestWithoutErrors) {
|
||||
ASSERT_TRUE(tests_.TestCallSetup(input_file, input_width, input_height,
|
||||
&local_file_renderer_,
|
||||
&remote_file_renderer_));
|
||||
|
||||
QualityMetricsResult psnr_result;
|
||||
int psnr_error = PsnrFromFiles(
|
||||
input_file.c_str(), remote_file_renderer_.GetFullOutputPath().c_str(),
|
||||
input_width, input_height, &psnr_result);
|
||||
|
||||
ASSERT_EQ(0, psnr_error) << "The PSNR routine failed - output files missing?";
|
||||
ASSERT_GT(psnr_result.average, 28); // That is, we want at least 28 dB
|
||||
|
||||
QualityMetricsResult ssim_result;
|
||||
int ssim_error = SsimFromFiles(
|
||||
input_file.c_str(), remote_file_renderer_.GetFullOutputPath().c_str(),
|
||||
input_width, input_height, &ssim_result);
|
||||
|
||||
ASSERT_EQ(0, ssim_error) << "The SSIM routine failed - output files missing?";
|
||||
ASSERT_GT(ssim_result.average, 0.95f); // 1 = perfect, -1 = terrible
|
||||
|
||||
ViETest::Log("Results: PSNR %f SSIM %f",
|
||||
psnr_result.average, ssim_result.average);
|
||||
}
|
||||
|
||||
TEST_F(ViEComparisonTest, RunsCodecTestWithoutErrors) {
|
||||
ASSERT_TRUE(tests_.TestCodecs(input_file, input_width, input_height,
|
||||
&local_file_renderer_,
|
||||
&remote_file_renderer_));
|
||||
|
||||
// We compare the local and remote here instead of with the original.
|
||||
// The reason is that it is hard to say when the three consecutive tests
|
||||
// switch over into each other, at which point we would have to restart the
|
||||
// original to get a fair comparison.
|
||||
QualityMetricsResult psnr_result;
|
||||
int psnr_error = PsnrFromFiles(
|
||||
input_file.c_str(),
|
||||
remote_file_renderer_.GetFullOutputPath().c_str(),
|
||||
input_width, input_height, &psnr_result);
|
||||
|
||||
ASSERT_EQ(0, psnr_error) << "The PSNR routine failed - output files missing?";
|
||||
// TODO(phoglund): This value should be higher. Investigate why the remote
|
||||
// file turns out 6 seconds shorter than the local file (frame dropping?...)
|
||||
EXPECT_GT(psnr_result.average, 20);
|
||||
|
||||
QualityMetricsResult ssim_result;
|
||||
int ssim_error = SsimFromFiles(
|
||||
local_file_renderer_.GetFullOutputPath().c_str(),
|
||||
remote_file_renderer_.GetFullOutputPath().c_str(),
|
||||
input_width, input_height, &ssim_result);
|
||||
|
||||
// TODO(phoglund): This value should also be higher.
|
||||
ASSERT_EQ(0, ssim_error) << "The SSIM routine failed - output files missing?";
|
||||
EXPECT_GT(ssim_result.average, 0.7f); // 1 = perfect, -1 = terrible
|
||||
|
||||
ViETest::Log("Results: PSNR %f SSIM %f",
|
||||
psnr_result.average, ssim_result.average);
|
||||
}
|
||||
|
||||
} // namespace
|
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* 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 "framedrop_primitives.h"
|
||||
#include "gflags/gflags.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "testsupport/fileutils.h"
|
||||
#include "testsupport/metrics/video_metrics.h"
|
||||
#include "vie_autotest.h"
|
||||
#include "vie_file_based_comparison_tests.h"
|
||||
#include "vie_integration_test_base.h"
|
||||
#include "vie_to_file_renderer.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// The input file must be QCIF since I420 gets scaled to that in the tests
|
||||
// (it is so bandwidth-heavy we have no choice). Our comparison algorithms
|
||||
// wouldn't like scaling, so this will work when we compare with the original.
|
||||
const int kInputWidth = 176;
|
||||
const int kInputHeight = 144;
|
||||
|
||||
const double kMinPSNR420DefaultBitRateQCIF = 28;
|
||||
const double kMinSSIM420DefaultBitRateQCIF = 0.95;
|
||||
const double kMinPSNRCodecTestsQCIF = 20;
|
||||
const double kMinSSIMCodecTestsQCIF = 0.7;
|
||||
const double kMinPSNR50kbpsQCIF = 25;
|
||||
const double kMinSSIM50kbpsQCIF = 0.8;
|
||||
|
||||
class ViEVideoVerificationTest : public testing::Test {
|
||||
protected:
|
||||
void SetUp() {
|
||||
input_file_ = webrtc::test::ResourcePath("paris_qcif", "yuv");
|
||||
local_file_renderer_ = new ViEToFileRenderer();
|
||||
remote_file_renderer_ = new ViEToFileRenderer();
|
||||
SetUpLocalFileRenderer(local_file_renderer_);
|
||||
SetUpRemoteFileRenderer(remote_file_renderer_);
|
||||
}
|
||||
|
||||
void TearDown() {
|
||||
TearDownFileRenderer(local_file_renderer_);
|
||||
delete local_file_renderer_;
|
||||
TearDownFileRenderer(remote_file_renderer_);
|
||||
delete remote_file_renderer_;
|
||||
}
|
||||
|
||||
void SetUpLocalFileRenderer(ViEToFileRenderer* file_renderer) {
|
||||
SetUpFileRenderer(file_renderer, "-local-preview.yuv");
|
||||
}
|
||||
|
||||
void SetUpRemoteFileRenderer(ViEToFileRenderer* file_renderer) {
|
||||
SetUpFileRenderer(file_renderer, "-remote.yuv");
|
||||
}
|
||||
|
||||
// Must be called manually inside the tests.
|
||||
void StopRenderers() {
|
||||
local_file_renderer_->StopRendering();
|
||||
remote_file_renderer_->StopRendering();
|
||||
}
|
||||
|
||||
void TearDownFileRenderer(ViEToFileRenderer* file_renderer) {
|
||||
bool test_failed = ::testing::UnitTest::GetInstance()->
|
||||
current_test_info()->result()->Failed();
|
||||
if (test_failed) {
|
||||
// Leave the files for analysis if the test failed.
|
||||
file_renderer->SaveOutputFile("failed-");
|
||||
} else {
|
||||
// No reason to keep the files if we succeeded.
|
||||
file_renderer->DeleteOutputFile();
|
||||
}
|
||||
}
|
||||
|
||||
void CompareFiles(const std::string& reference_file,
|
||||
const std::string& test_file,
|
||||
double minimum_psnr, double minimum_ssim) {
|
||||
QualityMetricsResult psnr;
|
||||
int psnr_error = PsnrFromFiles(reference_file.c_str(), test_file.c_str(),
|
||||
kInputWidth, kInputHeight, &psnr);
|
||||
ASSERT_EQ(0, psnr_error) << "PSNR routine failed - output files missing?";
|
||||
ASSERT_GT(psnr.average, minimum_psnr);
|
||||
|
||||
QualityMetricsResult ssim;
|
||||
int ssim_error = SsimFromFiles(reference_file.c_str(), test_file.c_str(),
|
||||
kInputWidth, kInputHeight, &ssim);
|
||||
ASSERT_EQ(0, ssim_error) << "SSIM routine failed - output files missing?";
|
||||
ASSERT_GT(ssim.average, minimum_ssim); // 1 = perfect, -1 = terrible
|
||||
|
||||
ViETest::Log("Results: PSNR: %f (db) SSIM: %f",
|
||||
psnr.average, ssim.average);
|
||||
}
|
||||
|
||||
std::string input_file_;
|
||||
ViEToFileRenderer* local_file_renderer_;
|
||||
ViEToFileRenderer* remote_file_renderer_;
|
||||
ViEFileBasedComparisonTests tests_;
|
||||
|
||||
private:
|
||||
void SetUpFileRenderer(ViEToFileRenderer* file_renderer,
|
||||
const std::string& suffix) {
|
||||
std::string test_case_name =
|
||||
::testing::UnitTest::GetInstance()->current_test_info()->name();
|
||||
|
||||
std::string output_path = ViETest::GetResultOutputPath();
|
||||
std::string filename = test_case_name + suffix;
|
||||
|
||||
if (!file_renderer->PrepareForRendering(output_path, filename)) {
|
||||
FAIL() << "Could not open output file " << filename <<
|
||||
" for writing.";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ViEVideoVerificationTest, RunsBaseStandardTestWithoutErrors) {
|
||||
ASSERT_TRUE(tests_.TestCallSetup(input_file_, kInputWidth, kInputHeight,
|
||||
local_file_renderer_,
|
||||
remote_file_renderer_));
|
||||
std::string output_file = remote_file_renderer_->GetFullOutputPath();
|
||||
StopRenderers();
|
||||
CompareFiles(input_file_, output_file, kMinPSNR420DefaultBitRateQCIF,
|
||||
kMinSSIM420DefaultBitRateQCIF);
|
||||
}
|
||||
|
||||
TEST_F(ViEVideoVerificationTest, RunsCodecTestWithoutErrors) {
|
||||
ASSERT_TRUE(tests_.TestCodecs(input_file_, kInputWidth, kInputHeight,
|
||||
local_file_renderer_,
|
||||
remote_file_renderer_));
|
||||
std::string reference_file = local_file_renderer_->GetFullOutputPath();
|
||||
std::string output_file = remote_file_renderer_->GetFullOutputPath();
|
||||
StopRenderers();
|
||||
|
||||
// We compare the local and remote here instead of with the original.
|
||||
// The reason is that it is hard to say when the three consecutive tests
|
||||
// switch over into each other, at which point we would have to restart the
|
||||
// original to get a fair comparison.
|
||||
CompareFiles(reference_file, output_file, kMinPSNRCodecTestsQCIF,
|
||||
kMinSSIMCodecTestsQCIF);
|
||||
|
||||
// TODO(phoglund): The values should be higher. Investigate why the remote
|
||||
// file turns out 6 seconds shorter than the local file (frame dropping?).
|
||||
}
|
||||
|
||||
// Runs a whole stack processing with tracking of which frames are dropped
|
||||
// in the encoder. The local and remote file will not be of equal size because
|
||||
// of unknown reasons. Tests show that they start at the same frame, which is
|
||||
// the important thing when doing frame-to-frame comparison with PSNR/SSIM.
|
||||
TEST_F(ViEVideoVerificationTest, RunsFullStackWithoutErrors) {
|
||||
// Use our own FrameDropMonitoringRemoteFileRenderer instead of the
|
||||
// ViEToFileRenderer from the test fixture:
|
||||
// TODO(kjellander): Find a better way to reuse this code without duplication.
|
||||
remote_file_renderer_->StopRendering();
|
||||
TearDownFileRenderer(remote_file_renderer_);
|
||||
delete remote_file_renderer_;
|
||||
|
||||
FrameDropDetector detector;
|
||||
remote_file_renderer_ = new FrameDropMonitoringRemoteFileRenderer(&detector);
|
||||
SetUpRemoteFileRenderer(remote_file_renderer_);
|
||||
|
||||
// Set a low bit rate so the encoder budget will be tight, causing it to drop
|
||||
// frames every now and then.
|
||||
const int kBitRateKbps = 50;
|
||||
ViETest::Log("Bit rate: %d kbps.\n", kBitRateKbps);
|
||||
tests_.TestFullStack(input_file_, kInputWidth, kInputHeight, kBitRateKbps,
|
||||
local_file_renderer_, remote_file_renderer_, &detector);
|
||||
const std::string reference_file = local_file_renderer_->GetFullOutputPath();
|
||||
const std::string output_file = remote_file_renderer_->GetFullOutputPath();
|
||||
StopRenderers();
|
||||
|
||||
ASSERT_EQ(detector.GetFramesDroppedAtRenderStep().size(),
|
||||
detector.GetFramesDroppedAtDecodeStep().size())
|
||||
<< "The number of dropped frames on the decode and render are not equal, "
|
||||
"this may be because we have a major problem in the jitter buffer?";
|
||||
|
||||
detector.PrintReport();
|
||||
|
||||
// We may have dropped frames during the processing, which means the output
|
||||
// file does not contain all the frames that are present in the input file.
|
||||
// To make the quality measurement correct, we must adjust the output file to
|
||||
// that by copying the last successful frame into the place where the dropped
|
||||
// frame would be, for all dropped frames.
|
||||
const int frame_length_in_bytes = 3 * kInputHeight * kInputWidth / 2;
|
||||
int num_frames = detector.NumberSentFrames();
|
||||
ViETest::Log("Frame length: %d bytes\n", frame_length_in_bytes);
|
||||
FixOutputFileForComparison(output_file, num_frames, frame_length_in_bytes,
|
||||
detector.GetFramesDroppedAtDecodeStep());
|
||||
|
||||
// Verify all sent frames are present in the output file.
|
||||
size_t output_file_size = webrtc::test::GetFileSize(output_file);
|
||||
EXPECT_EQ(num_frames,
|
||||
static_cast<int>(output_file_size / frame_length_in_bytes))
|
||||
<< "The output file size is incorrect. It should be equal to the number"
|
||||
"of frames multiplied by the frame size. This will likely affect "
|
||||
"PSNR/SSIM calculations in a bad way.";
|
||||
CompareFiles(reference_file, output_file, kMinPSNR50kbpsQCIF,
|
||||
kMinSSIM50kbpsQCIF);
|
||||
}
|
||||
|
||||
} // namespace
|
@ -62,9 +62,7 @@ bool ViEToFileRenderer::DeleteOutputFile() {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ViEToFileRenderer::GetFullOutputPath() const {
|
||||
assert(output_file_ != NULL);
|
||||
|
||||
const std::string ViEToFileRenderer::GetFullOutputPath() const {
|
||||
return output_path_ + output_filename_;
|
||||
}
|
||||
|
||||
|
@ -8,14 +8,13 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef SRC_VIDEO_ENGINE_MAIN_TEST_AUTOTEST_HELPERS_VIE_TO_FILE_RENDERER_H_
|
||||
#define SRC_VIDEO_ENGINE_MAIN_TEST_AUTOTEST_HELPERS_VIE_TO_FILE_RENDERER_H_
|
||||
#ifndef SRC_VIDEO_ENGINE_TEST_AUTO_TEST_HELPERS_VIE_TO_FILE_RENDERER_H_
|
||||
#define SRC_VIDEO_ENGINE_TEST_AUTO_TEST_HELPERS_VIE_TO_FILE_RENDERER_H_
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "vie_render.h"
|
||||
#include "video_engine/main/interface/vie_render.h"
|
||||
|
||||
class ViEToFileRenderer: public webrtc::ExternalRenderer {
|
||||
public:
|
||||
@ -46,7 +45,7 @@ class ViEToFileRenderer: public webrtc::ExternalRenderer {
|
||||
int DeliverFrame(unsigned char* buffer, int buffer_size,
|
||||
unsigned int time_stamp);
|
||||
|
||||
std::string GetFullOutputPath() const;
|
||||
const std::string GetFullOutputPath() const;
|
||||
|
||||
private:
|
||||
void ForgetOutputFile();
|
||||
@ -56,4 +55,4 @@ class ViEToFileRenderer: public webrtc::ExternalRenderer {
|
||||
std::string output_filename_;
|
||||
};
|
||||
|
||||
#endif // SRC_VIDEO_ENGINE_MAIN_TEST_AUTOTEST_HELPERS_VIE_TO_FILE_RENDERER_H_
|
||||
#endif // SRC_VIDEO_ENGINE_TEST_AUTO_TEST_HELPERS_VIE_TO_FILE_RENDERER_H_
|
||||
|
@ -8,11 +8,12 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef SRC_VIDEO_ENGINE_MAIN_TEST_AUTOTEST_INTERFACE_VIE_COMPARISON_TESTS_H_
|
||||
#define SRC_VIDEO_ENGINE_MAIN_TEST_AUTOTEST_INTERFACE_VIE_COMPARISON_TESTS_H_
|
||||
#ifndef SRC_VIDEO_ENGINE_TEST_AUTO_TEST_INTERFACE_VIE_COMPARISON_TESTS_H_
|
||||
#define SRC_VIDEO_ENGINE_TEST_AUTO_TEST_INTERFACE_VIE_COMPARISON_TESTS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
class FrameDropDetector;
|
||||
class ViEToFileRenderer;
|
||||
|
||||
// This class contains comparison tests, which will exercise video engine
|
||||
@ -29,7 +30,7 @@ class ViEToFileRenderer;
|
||||
// The local preview is a straight, unaltered copy of the input. This can be
|
||||
// useful for comparisons if the test method contains several stages where the
|
||||
// input is restarted between stages.
|
||||
class ViEComparisonTests {
|
||||
class ViEFileBasedComparisonTests {
|
||||
public:
|
||||
// Test a typical simple call setup. Returns false if the input file
|
||||
// could not be opened; reports errors using googletest macros otherwise.
|
||||
@ -49,6 +50,17 @@ class ViEComparisonTests {
|
||||
int height,
|
||||
ViEToFileRenderer* local_file_renderer,
|
||||
ViEToFileRenderer* remote_file_renderer);
|
||||
|
||||
// Runs a full stack test using the VP8 codec. Tests the full stack and uses
|
||||
// RTP timestamps to sync frames between the endpoints.
|
||||
void TestFullStack(
|
||||
const std::string& i420_video_file,
|
||||
int width,
|
||||
int height,
|
||||
int bit_rate_kbps,
|
||||
ViEToFileRenderer* local_file_renderer,
|
||||
ViEToFileRenderer* remote_file_renderer,
|
||||
FrameDropDetector* frame_drop_detector);
|
||||
};
|
||||
|
||||
#endif // SRC_VIDEO_ENGINE_MAIN_TEST_AUTOTEST_INTERFACE_VIE_COMPARISON_TESTS_H_
|
||||
#endif // SRC_VIDEO_ENGINE_TEST_AUTO_TEST_INTERFACE_VIE_COMPARISON_TESTS_H_
|
@ -17,28 +17,7 @@
|
||||
#include "video_capture_factory.h"
|
||||
#include "tb_interfaces.h"
|
||||
|
||||
// Helper functions
|
||||
|
||||
void SetSuitableResolution(webrtc::VideoCodec* video_codec,
|
||||
int forced_codec_width,
|
||||
int forced_codec_height) {
|
||||
if (forced_codec_width != kDoNotForceResolution &&
|
||||
forced_codec_height != kDoNotForceResolution) {
|
||||
video_codec->width = forced_codec_width;
|
||||
video_codec->height = forced_codec_height;
|
||||
} else if (video_codec->codecType == webrtc::kVideoCodecI420) {
|
||||
// I420 is very bandwidth heavy, so limit it here.
|
||||
video_codec->width = 176;
|
||||
video_codec->height = 144;
|
||||
} else if (video_codec->codecType == webrtc::kVideoCodecH263) {
|
||||
video_codec->width = 352;
|
||||
video_codec->height = 288;
|
||||
} else {
|
||||
// Otherwise go with 640x480.
|
||||
video_codec->width = 640;
|
||||
video_codec->height = 480;
|
||||
}
|
||||
}
|
||||
// Helper functions.
|
||||
|
||||
void TestCodecImageProcess(webrtc::VideoCodec video_codec,
|
||||
webrtc::ViECodec* codec_interface,
|
||||
@ -46,7 +25,7 @@ void TestCodecImageProcess(webrtc::VideoCodec video_codec,
|
||||
webrtc::ViEImageProcess* image_process) {
|
||||
|
||||
EXPECT_EQ(0, codec_interface->SetSendCodec(video_channel, video_codec));
|
||||
ViEAutoTestEffectFilter frame_counter;
|
||||
FrameCounterEffectFilter frame_counter;
|
||||
EXPECT_EQ(0, image_process->RegisterRenderEffectFilter(video_channel,
|
||||
frame_counter));
|
||||
AutoTestSleep (KAutoTestSleepTimeMs);
|
||||
@ -202,6 +181,5 @@ void SetSendCodec(webrtc::VideoCodecType of_type,
|
||||
}
|
||||
|
||||
SetSuitableResolution(&codec, forced_codec_width, forced_codec_height);
|
||||
|
||||
EXPECT_EQ(0, codec_interface->SetSendCodec(video_channel, codec));
|
||||
}
|
||||
|
@ -8,24 +8,24 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef SRC_VIDEO_ENGINE_MAIN_TEST_AUTOTEST_PRIMITIVES_CODEC_PRIMITIVES_H_
|
||||
#define SRC_VIDEO_ENGINE_MAIN_TEST_AUTOTEST_PRIMITIVES_CODEC_PRIMITIVES_H_
|
||||
#ifndef WEBRTC_VIDEO_ENGINE_TEST_AUTO_TEST_PRIMITIVES_CODEC_PRIMITIVES_H_
|
||||
#define WEBRTC_VIDEO_ENGINE_TEST_AUTO_TEST_PRIMITIVES_CODEC_PRIMITIVES_H_
|
||||
|
||||
#include "vie_autotest_defines.h"
|
||||
#include "vie_codec.h"
|
||||
#include "vie_image_process.h"
|
||||
#include "video_engine/main/interface/vie_codec.h"
|
||||
#include "video_engine/main/interface/vie_image_process.h"
|
||||
#include "video_engine/test/auto_test/interface/vie_autotest_defines.h"
|
||||
#include "video_engine/test/auto_test/primitives/general_primitives.h"
|
||||
|
||||
class TbInterfaces;
|
||||
|
||||
// This can be passed into TestCodecs and SetSendCodec
|
||||
// in order to let the function choose resolutions itself.
|
||||
const int kDoNotForceResolution = 0;
|
||||
|
||||
// Tests that a codec actually renders frames by registering a basic
|
||||
// render effect filter on the codec and then running it. This test is
|
||||
// quite lenient on the number of frames that get rendered, so it should not
|
||||
// be seen as a end-user-visible quality measure - it is more a sanity check
|
||||
// that the codec at least gets some frames through.
|
||||
|
||||
// The codec resolution can be forced by specifying the forced* variables
|
||||
// (pass in kDoNotForceResolution if you don't care).
|
||||
void TestCodecs(const TbInterfaces& interfaces,
|
||||
int capture_id,
|
||||
int video_channel,
|
||||
@ -35,6 +35,9 @@ void TestCodecs(const TbInterfaces& interfaces,
|
||||
// This helper function will set the send codec in the codec interface to a
|
||||
// codec of the specified type. It will generate a test failure if we do not
|
||||
// support the provided codec type.
|
||||
|
||||
// The codec resolution can be forced by specifying the forced* variables
|
||||
// (pass in kDoNotForceResolution if you don't care).
|
||||
void SetSendCodec(webrtc::VideoCodecType of_type,
|
||||
webrtc::ViECodec* codec_interface,
|
||||
int video_channel,
|
||||
@ -102,14 +105,14 @@ class ViEAutotestCodecObserver: public webrtc::ViEEncoderObserver,
|
||||
}
|
||||
};
|
||||
|
||||
class ViEAutoTestEffectFilter: public webrtc::ViEEffectFilter
|
||||
class FrameCounterEffectFilter : public webrtc::ViEEffectFilter
|
||||
{
|
||||
public:
|
||||
int numFrames;
|
||||
ViEAutoTestEffectFilter() {
|
||||
FrameCounterEffectFilter() {
|
||||
numFrames = 0;
|
||||
}
|
||||
virtual ~ViEAutoTestEffectFilter() {
|
||||
virtual ~FrameCounterEffectFilter() {
|
||||
}
|
||||
|
||||
virtual int Transform(int size, unsigned char* frameBuffer,
|
||||
@ -120,4 +123,4 @@ class ViEAutoTestEffectFilter: public webrtc::ViEEffectFilter
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SRC_VIDEO_ENGINE_MAIN_TEST_AUTOTEST_PRIMITIVES_CODEC_PRIMITIVES_H_
|
||||
#endif // WEBRTC_VIDEO_ENGINE_TEST_AUTO_TEST_PRIMITIVES_CODEC_PRIMITIVES_H_
|
||||
|
@ -0,0 +1,314 @@
|
||||
/*
|
||||
* 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 <cassert>
|
||||
#include <string>
|
||||
|
||||
#include "framedrop_primitives.h"
|
||||
|
||||
#include "general_primitives.h"
|
||||
#include "system_wrappers/interface/tick_util.h"
|
||||
#include "tb_interfaces.h"
|
||||
#include "testsupport/fileutils.h"
|
||||
#include "testsupport/frame_reader.h"
|
||||
#include "testsupport/frame_writer.h"
|
||||
#include "video_capture_factory.h"
|
||||
#include "vie_autotest.h"
|
||||
#include "vie_autotest_defines.h"
|
||||
#include "vie_to_file_renderer.h"
|
||||
|
||||
// Tracks which frames are sent on the local side and reports them to the
|
||||
// FrameDropDetector class.
|
||||
class SendTimestampEffectFilter: public webrtc::ViEEffectFilter {
|
||||
public:
|
||||
explicit SendTimestampEffectFilter(FrameDropDetector* frame_drop_detector)
|
||||
: frame_drop_detector_(frame_drop_detector) {}
|
||||
virtual ~SendTimestampEffectFilter() {}
|
||||
virtual int Transform(int size, unsigned char* frameBuffer,
|
||||
unsigned int timeStamp90KHz, unsigned int width,
|
||||
unsigned int height) {
|
||||
frame_drop_detector_->ReportSent(timeStamp90KHz);
|
||||
return 0;
|
||||
}
|
||||
private:
|
||||
FrameDropDetector* frame_drop_detector_;
|
||||
};
|
||||
|
||||
// Tracks when frames are decoded on the remote side (received from the
|
||||
// jitter buffer) and reports them to the FrameDropDetector class.
|
||||
class DecodeTimestampEffectFilter: public webrtc::ViEEffectFilter {
|
||||
public:
|
||||
explicit DecodeTimestampEffectFilter(FrameDropDetector* frame_drop_detector)
|
||||
: frame_drop_detector_(frame_drop_detector) {}
|
||||
virtual ~DecodeTimestampEffectFilter() {}
|
||||
virtual int Transform(int size, unsigned char* frameBuffer,
|
||||
unsigned int timeStamp90KHz, unsigned int width,
|
||||
unsigned int height) {
|
||||
frame_drop_detector_->ReportDecoded(timeStamp90KHz);
|
||||
return 0;
|
||||
}
|
||||
private:
|
||||
FrameDropDetector* frame_drop_detector_;
|
||||
};
|
||||
|
||||
void TestFullStack(const TbInterfaces& interfaces,
|
||||
int capture_id,
|
||||
int video_channel,
|
||||
int width,
|
||||
int height,
|
||||
int bit_rate_kbps,
|
||||
FrameDropDetector* frame_drop_detector) {
|
||||
webrtc::VideoEngine *video_engine_interface = interfaces.video_engine;
|
||||
webrtc::ViEBase *base_interface = interfaces.base;
|
||||
webrtc::ViECapture *capture_interface = interfaces.capture;
|
||||
webrtc::ViERender *render_interface = interfaces.render;
|
||||
webrtc::ViECodec *codec_interface = interfaces.codec;
|
||||
webrtc::ViENetwork *network_interface = interfaces.network;
|
||||
|
||||
// ***************************************************************
|
||||
// Engine ready. Begin testing class
|
||||
// ***************************************************************
|
||||
webrtc::VideoCodec video_codec;
|
||||
memset(&video_codec, 0, sizeof (webrtc::VideoCodec));
|
||||
|
||||
// Set up all receive codecs. This basically setup the codec interface
|
||||
// to be able to recognize all receive codecs based on payload type.
|
||||
for (int idx = 0; idx < codec_interface->NumberOfCodecs(); idx++) {
|
||||
EXPECT_EQ(0, codec_interface->GetCodec(idx, video_codec));
|
||||
SetSuitableResolution(&video_codec, width, height);
|
||||
|
||||
EXPECT_EQ(0, codec_interface->SetReceiveCodec(video_channel, video_codec));
|
||||
}
|
||||
const char *ip_address = "127.0.0.1";
|
||||
const unsigned short rtp_port = 6000;
|
||||
EXPECT_EQ(0, network_interface->SetLocalReceiver(video_channel, rtp_port));
|
||||
EXPECT_EQ(0, base_interface->StartReceive(video_channel));
|
||||
EXPECT_EQ(0, network_interface->SetSendDestination(video_channel, ip_address,
|
||||
rtp_port));
|
||||
// Setup only the VP8 codec, which is what we'll use.
|
||||
webrtc::VideoCodec codec;
|
||||
EXPECT_TRUE(FindSpecificCodec(webrtc::kVideoCodecVP8, codec_interface,
|
||||
&codec));
|
||||
codec.startBitrate = bit_rate_kbps;
|
||||
codec.maxBitrate = bit_rate_kbps;
|
||||
codec.width = width;
|
||||
codec.height = height;
|
||||
EXPECT_EQ(0, codec_interface->SetSendCodec(video_channel, codec));
|
||||
|
||||
webrtc::ViEImageProcess *image_process =
|
||||
webrtc::ViEImageProcess::GetInterface(video_engine_interface);
|
||||
EXPECT_TRUE(image_process);
|
||||
|
||||
// Setup the effect filters
|
||||
DecodeTimestampEffectFilter decode_filter(frame_drop_detector);
|
||||
EXPECT_EQ(0, image_process->RegisterRenderEffectFilter(video_channel,
|
||||
decode_filter));
|
||||
SendTimestampEffectFilter send_filter(frame_drop_detector);
|
||||
EXPECT_EQ(0, image_process->RegisterSendEffectFilter(video_channel,
|
||||
send_filter));
|
||||
// Send video.
|
||||
EXPECT_EQ(0, base_interface->StartSend(video_channel));
|
||||
AutoTestSleep(KAutoTestSleepTimeMs);
|
||||
|
||||
// Cleanup.
|
||||
EXPECT_EQ(0, image_process->DeregisterRenderEffectFilter(video_channel));
|
||||
EXPECT_EQ(0, image_process->DeregisterSendEffectFilter(video_channel));
|
||||
image_process->Release();
|
||||
ViETest::Log("Done!");
|
||||
|
||||
// ***************************************************************
|
||||
// Testing finished. Tear down Video Engine
|
||||
// ***************************************************************
|
||||
EXPECT_EQ(0, base_interface->StopSend(video_channel));
|
||||
EXPECT_EQ(0, base_interface->StopReceive(video_channel));
|
||||
EXPECT_EQ(0, render_interface->StopRender(capture_id));
|
||||
EXPECT_EQ(0, render_interface->StopRender(video_channel));
|
||||
EXPECT_EQ(0, render_interface->RemoveRenderer(capture_id));
|
||||
EXPECT_EQ(0, render_interface->RemoveRenderer(video_channel));
|
||||
EXPECT_EQ(0, capture_interface->DisconnectCaptureDevice(video_channel));
|
||||
EXPECT_EQ(0, base_interface->DeleteChannel(video_channel));
|
||||
}
|
||||
|
||||
void FixOutputFileForComparison(const std::string& output_file,
|
||||
int total_number_of_frames,
|
||||
int frame_length_in_bytes,
|
||||
std::list<Frame*> dropped_frames) {
|
||||
if (dropped_frames.size() == 0) {
|
||||
// No need to modify if no frames are dropped, since the file is already
|
||||
// frame-per-frame in sync in that case.
|
||||
return;
|
||||
}
|
||||
webrtc::test::FrameReaderImpl frame_reader(output_file,
|
||||
frame_length_in_bytes);
|
||||
const std::string temp_file = output_file + ".fixed";
|
||||
webrtc::test::FrameWriterImpl frame_writer(temp_file, frame_length_in_bytes);
|
||||
frame_reader.Init();
|
||||
frame_writer.Init();
|
||||
|
||||
// Assume the dropped_frames list is sorted by frame number.
|
||||
int next_dropped_frame = dropped_frames.front()->number_;
|
||||
dropped_frames.pop_front();
|
||||
ASSERT_NE(0, next_dropped_frame) << "It should not be possible to drop the "
|
||||
"first frame. Both because we don't have anything useful to fill that "
|
||||
"gap with and it is impossible to detect it without any previous "
|
||||
"timestamps to compare with.";
|
||||
|
||||
WebRtc_UWord8* last_read_frame_data =
|
||||
new WebRtc_UWord8[frame_length_in_bytes];
|
||||
|
||||
// Write the first frame now since it will always be the same.
|
||||
EXPECT_TRUE(frame_reader.ReadFrame(last_read_frame_data));
|
||||
EXPECT_TRUE(frame_writer.WriteFrame(last_read_frame_data));
|
||||
|
||||
// Process the file and write frame duplicates for all dropped frames.
|
||||
for (int i = 1; i < total_number_of_frames; ++i) {
|
||||
if (i == next_dropped_frame) {
|
||||
// Write the previous frame to the output file:
|
||||
EXPECT_TRUE(frame_writer.WriteFrame(last_read_frame_data));
|
||||
if (!dropped_frames.empty()) {
|
||||
next_dropped_frame = dropped_frames.front()->number_;
|
||||
dropped_frames.pop_front();
|
||||
}
|
||||
} else {
|
||||
// Read a new frame and write it to the output file.
|
||||
EXPECT_TRUE(frame_reader.ReadFrame(last_read_frame_data));
|
||||
EXPECT_TRUE(frame_writer.WriteFrame(last_read_frame_data));
|
||||
}
|
||||
}
|
||||
delete[] last_read_frame_data;
|
||||
frame_reader.Close();
|
||||
frame_writer.Close();
|
||||
ASSERT_EQ(0, std::remove(output_file.c_str()));
|
||||
ASSERT_EQ(0, std::rename(temp_file.c_str(), output_file.c_str()));
|
||||
}
|
||||
|
||||
void FrameDropDetector::ReportSent(unsigned int timestamp) {
|
||||
int number = sent_frames_list_.size();
|
||||
Frame* frame = new Frame(number, timestamp);
|
||||
frame->sent_timestamp_in_us_ = webrtc::TickTime::MicrosecondTimestamp();
|
||||
sent_frames_list_.push_back(frame);
|
||||
sent_frames_[timestamp] = frame;
|
||||
}
|
||||
|
||||
void FrameDropDetector::ReportDecoded(unsigned int timestamp) {
|
||||
// When the first sent frame arrives we calculate the fixed difference
|
||||
// between the timestamps of the sent frames and the decoded/rendered frames.
|
||||
// This diff is then used to identify the frames from the sent_frames_ map.
|
||||
if (frame_timestamp_diff_ == 0) {
|
||||
Frame* first_sent_frame = sent_frames_list_.front();
|
||||
frame_timestamp_diff_ = timestamp - first_sent_frame->frame_timestamp_;
|
||||
}
|
||||
// Calculate the sent timestamp required to identify the frame:
|
||||
unsigned int sent_timestamp = timestamp - frame_timestamp_diff_;
|
||||
|
||||
// Find the right Frame object in the map of sent frames:
|
||||
Frame* frame = sent_frames_[sent_timestamp];
|
||||
frame->decoded_timestamp_in_us_ = webrtc::TickTime::MicrosecondTimestamp();
|
||||
decoded_frames_[sent_timestamp] = frame;
|
||||
}
|
||||
|
||||
void FrameDropDetector::ReportRendered(unsigned int timestamp) {
|
||||
// Calculate the sent timestamp required to identify the frame:
|
||||
unsigned int sent_timestamp = timestamp - frame_timestamp_diff_;
|
||||
|
||||
// Find this frame in the map of sent frames:
|
||||
Frame* frame = sent_frames_[sent_timestamp];
|
||||
frame->rendered_timestamp_in_us_ = webrtc::TickTime::MicrosecondTimestamp();
|
||||
rendered_frames_[sent_timestamp] = frame;
|
||||
}
|
||||
|
||||
int FrameDropDetector::NumberSentFrames() {
|
||||
return static_cast<int>(sent_frames_.size());
|
||||
}
|
||||
|
||||
void FrameDropDetector::PrintReport() {
|
||||
ViETest::Log("Frame Drop Detector report:");
|
||||
ViETest::Log("Sent frames: %ld", sent_frames_.size());
|
||||
ViETest::Log("Decoded frames: %ld", decoded_frames_.size());
|
||||
ViETest::Log("Rendered frames: %ld", rendered_frames_.size());
|
||||
|
||||
// Display all frames and stats for them:
|
||||
long last_sent = 0;
|
||||
long last_decoded = 0;
|
||||
long last_rendered = 0;
|
||||
ViETest::Log("Sent frames summary:");
|
||||
ViETest::Log("Deltas are in microseconds and only cover existing frames.");
|
||||
ViETest::Log("Frame no SentDelta DecodedDelta RenderedDelta DecodedDrop? "
|
||||
"RenderedDrop?");
|
||||
for (std::list<Frame*>::iterator it = sent_frames_list_.begin();
|
||||
it != sent_frames_list_.end(); it++) {
|
||||
bool dropped_decode = (decoded_frames_.find((*it)->frame_timestamp_) ==
|
||||
decoded_frames_.end());
|
||||
bool dropped_render = (rendered_frames_.find((*it)->frame_timestamp_) ==
|
||||
rendered_frames_.end());
|
||||
int sent_delta = static_cast<int>((*it)->sent_timestamp_in_us_ - last_sent);
|
||||
int decoded_delta = dropped_decode ? 0 :
|
||||
static_cast<int>((*it)->decoded_timestamp_in_us_ - last_decoded);
|
||||
int rendered_delta = dropped_render ? 0 :
|
||||
static_cast<int>((*it)->rendered_timestamp_in_us_ - last_rendered);
|
||||
|
||||
// Set values to 0 for the first frame:
|
||||
if ((*it)->number_ == 0) {
|
||||
sent_delta = 0;
|
||||
decoded_delta = 0;
|
||||
rendered_delta = 0;
|
||||
}
|
||||
ViETest::Log("%8d %10d %10d %10d %s %s", (*it)->number_,
|
||||
sent_delta, decoded_delta, rendered_delta,
|
||||
dropped_decode ? "DROPPED" : " ",
|
||||
dropped_render ? "DROPPED" : " ");
|
||||
last_sent = (*it)->sent_timestamp_in_us_;
|
||||
if (!dropped_render) {
|
||||
last_decoded = (*it)->decoded_timestamp_in_us_;
|
||||
last_rendered = (*it)->rendered_timestamp_in_us_;
|
||||
}
|
||||
}
|
||||
// Find and print the dropped frames. Work at a copy of the sent_frames_ map.
|
||||
std::list<Frame*> decode_dropped_frames = GetFramesDroppedAtDecodeStep();
|
||||
ViETest::Log("Number of dropped frames at the decode step: %d",
|
||||
static_cast<int>(decode_dropped_frames.size()));
|
||||
std::list<Frame*> render_dropped_frames = GetFramesDroppedAtRenderStep();
|
||||
ViETest::Log("Number of dropped frames at the render step: %d",
|
||||
static_cast<int>(render_dropped_frames.size()));
|
||||
}
|
||||
|
||||
const std::list<Frame*> FrameDropDetector::GetFramesDroppedAtDecodeStep() {
|
||||
std::list<Frame*> dropped_frames;
|
||||
std::map<unsigned int, Frame*>::iterator it;
|
||||
for (it = sent_frames_.begin(); it != sent_frames_.end(); it++) {
|
||||
if (decoded_frames_.find(it->first) == decoded_frames_.end()) {
|
||||
dropped_frames.push_back(it->second);
|
||||
}
|
||||
}
|
||||
return dropped_frames;
|
||||
}
|
||||
|
||||
const std::list<Frame*> FrameDropDetector::GetFramesDroppedAtRenderStep() {
|
||||
std::list<Frame*> dropped_frames;
|
||||
std::map<unsigned int, Frame*>::iterator it;
|
||||
for (it = sent_frames_.begin(); it != sent_frames_.end(); it++) {
|
||||
if (rendered_frames_.find(it->first) == rendered_frames_.end()) {
|
||||
dropped_frames.push_back(it->second);
|
||||
}
|
||||
}
|
||||
return dropped_frames;
|
||||
}
|
||||
|
||||
int FrameDropMonitoringRemoteFileRenderer::DeliverFrame(
|
||||
unsigned char *buffer, int buffer_size, unsigned int time_stamp) {
|
||||
// Register that this frame has been rendered:
|
||||
frame_drop_detector_->ReportRendered(time_stamp);
|
||||
return ViEToFileRenderer::DeliverFrame(buffer, buffer_size, time_stamp);
|
||||
}
|
||||
|
||||
int FrameDropMonitoringRemoteFileRenderer::FrameSizeChange(
|
||||
unsigned int width, unsigned int height, unsigned int number_of_streams) {
|
||||
return ViEToFileRenderer::FrameSizeChange(width, height, number_of_streams);
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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 WEBRTC_VIDEO_ENGINE_TEST_AUTO_TEST_SOURCE_FRAMEDROP_PRIMITIVES_H_
|
||||
#define WEBRTC_VIDEO_ENGINE_TEST_AUTO_TEST_SOURCE_FRAMEDROP_PRIMITIVES_H_
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
#include "video_engine/main/interface/vie_codec.h"
|
||||
#include "video_engine/main/interface/vie_image_process.h"
|
||||
#include "video_engine/test/auto_test/interface/vie_autotest_defines.h"
|
||||
#include "video_engine/test/auto_test/helpers/vie_to_file_renderer.h"
|
||||
|
||||
class FrameDropDetector;
|
||||
class TbInterfaces;
|
||||
|
||||
// Initializes the Video engine and its components, runs video playback using
|
||||
// for KAutoTestSleepTimeMs milliseconds, then shuts down everything.
|
||||
// The bit rate should be low enough to make the video encoder being forced to
|
||||
// drop some frames, in order to test the frame drop detection that is performed
|
||||
// by the FrameDropDetector class.
|
||||
void TestFullStack(const TbInterfaces& interfaces,
|
||||
int capture_id,
|
||||
int video_channel,
|
||||
int width,
|
||||
int height,
|
||||
int bit_rate_kbps,
|
||||
FrameDropDetector* frame_drop_detector);
|
||||
|
||||
// A frame in a video file. The three different points in the stack when
|
||||
// register the frame state are (in time order): sent, decoded, rendered.
|
||||
class Frame {
|
||||
public:
|
||||
Frame(int number, unsigned int timestamp)
|
||||
: number_(number), frame_timestamp_(timestamp),
|
||||
sent_timestamp_in_us_(0), decoded_timestamp_in_us_(0),
|
||||
rendered_timestamp_in_us_(0) {}
|
||||
|
||||
// Frame number, starting at 0.
|
||||
int number_;
|
||||
|
||||
// Frame timestamp, that is used by Video Engine and RTP headers and set when
|
||||
// the frame is sent into the stack.
|
||||
unsigned int frame_timestamp_;
|
||||
|
||||
// Timestamps for our measurements of when the frame is in different states.
|
||||
int64_t sent_timestamp_in_us_;
|
||||
int64_t decoded_timestamp_in_us_;
|
||||
int64_t rendered_timestamp_in_us_;
|
||||
};
|
||||
|
||||
// Fixes the output file by copying the last successful frame into the place
|
||||
// where the dropped frame would be, for all dropped frames (if any).
|
||||
// This method will not be able to fix data for the first frame if that is
|
||||
// dropped, since there'll be no previous frame to copy. This case should never
|
||||
// happen because of encoder frame dropping at least.
|
||||
// Parameters:
|
||||
// output_file The output file to modify (pad with frame copies
|
||||
// for all dropped frames)
|
||||
// total_number_of_frames Number of frames in the reference file we want
|
||||
// to match.
|
||||
// frame_length_in_bytes Byte length of each frame.
|
||||
// dropped_frames List of Frame objects. Must be sorted by frame
|
||||
// number. If empty this method will do nothing.
|
||||
void FixOutputFileForComparison(const std::string& output_file,
|
||||
int total_number_of_frames,
|
||||
int frame_length_in_bytes,
|
||||
std::list<Frame*> dropped_frames);
|
||||
|
||||
// Handles statistics about dropped frames. Frames travel through the stack
|
||||
// with different timestamps. The sent frames have one timestamp on the sending
|
||||
// side while the decoded/rendered frames have another timestamp on the
|
||||
// receiving side. However the difference between these timestamps is fixed,
|
||||
// which we can use to identify the frames when they arrive, since the
|
||||
// FrameDropDetector class gets data reported from both sides.
|
||||
// The three different points in the stack when this class examines the frame
|
||||
// states are (in time order): sent, decoded, rendered.
|
||||
class FrameDropDetector {
|
||||
public:
|
||||
FrameDropDetector()
|
||||
: frame_timestamp_diff_(0) {}
|
||||
|
||||
// Report a frame being sent; the first step of a frame transfer.
|
||||
// This timestamp becomes the frame timestamp in the Frame objects.
|
||||
void ReportSent(unsigned int timestamp);
|
||||
|
||||
// Report a frame being rendered; happens right before it is received.
|
||||
// This timestamp differs from the one in ReportSent timestamp.
|
||||
void ReportDecoded(unsigned int timestamp);
|
||||
|
||||
// Report a frame being rendered; the last step of a frame transfer.
|
||||
// This timestamp differs from the one in ReportSent timestamp, but is the
|
||||
// same as the ReportRendered timestamp.
|
||||
void ReportRendered(unsigned int timestamp);
|
||||
|
||||
// The number of sent frames, i.e. the number of times the ReportSent has been
|
||||
// called successfully.
|
||||
int NumberSentFrames();
|
||||
|
||||
// Calculates which frames have been registered as dropped at the decode step.
|
||||
const std::list<Frame*> GetFramesDroppedAtDecodeStep();
|
||||
|
||||
// Calculates which frames have been registered as dropped at the render step.
|
||||
const std::list<Frame*> GetFramesDroppedAtRenderStep();
|
||||
|
||||
// Prints a detailed report about all the different frame states and which
|
||||
// ones are detected as dropped, using ViETest::Log.
|
||||
void PrintReport();
|
||||
|
||||
private:
|
||||
// Maps mapping frame timestamps to Frame objects.
|
||||
std::map<unsigned int, Frame*> sent_frames_;
|
||||
std::map<unsigned int, Frame*> decoded_frames_;
|
||||
std::map<unsigned int, Frame*> rendered_frames_;
|
||||
|
||||
// A list with the frames sorted in their sent order:
|
||||
std::list<Frame*> sent_frames_list_;
|
||||
|
||||
// The constant diff between the sent and rendered frames, since their
|
||||
// timestamps are converted.
|
||||
unsigned int frame_timestamp_diff_;
|
||||
};
|
||||
|
||||
// Tracks which frames are received on the remote side and reports back to the
|
||||
// FrameDropDetector class when they are rendered.
|
||||
class FrameDropMonitoringRemoteFileRenderer : public ViEToFileRenderer {
|
||||
public:
|
||||
explicit FrameDropMonitoringRemoteFileRenderer(
|
||||
FrameDropDetector* frame_drop_detector)
|
||||
: frame_drop_detector_(frame_drop_detector) {}
|
||||
virtual ~FrameDropMonitoringRemoteFileRenderer() {}
|
||||
|
||||
// Implementation of ExternalRenderer:
|
||||
int FrameSizeChange(unsigned int width, unsigned int height,
|
||||
unsigned int number_of_streams);
|
||||
int DeliverFrame(unsigned char* buffer, int buffer_size,
|
||||
unsigned int time_stamp);
|
||||
private:
|
||||
FrameDropDetector* frame_drop_detector_;
|
||||
};
|
||||
|
||||
#endif // WEBRTC_VIDEO_ENGINE_TEST_AUTO_TEST_SOURCE_FRAMEDROP_PRIMITIVES_H_
|
@ -111,3 +111,23 @@ bool FindSpecificCodec(webrtc::VideoCodecType of_type,
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetSuitableResolution(webrtc::VideoCodec* video_codec,
|
||||
int forced_codec_width,
|
||||
int forced_codec_height) {
|
||||
if (forced_codec_width != kDoNotForceResolution &&
|
||||
forced_codec_height != kDoNotForceResolution) {
|
||||
video_codec->width = forced_codec_width;
|
||||
video_codec->height = forced_codec_height;
|
||||
} else if (video_codec->codecType == webrtc::kVideoCodecI420) {
|
||||
// I420 is very bandwidth heavy, so limit it here.
|
||||
video_codec->width = 176;
|
||||
video_codec->height = 144;
|
||||
} else if (video_codec->codecType == webrtc::kVideoCodecH263) {
|
||||
video_codec->width = 352;
|
||||
video_codec->height = 288;
|
||||
} else {
|
||||
// Otherwise go with 640x480.
|
||||
video_codec->width = 640;
|
||||
video_codec->height = 480;
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,8 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef SRC_VIDEO_ENGINE_MAIN_TEST_AUTOTEST_PRIMITIVES_GENERAL_PRIMITIVES_H_
|
||||
#define SRC_VIDEO_ENGINE_MAIN_TEST_AUTOTEST_PRIMITIVES_GENERAL_PRIMITIVES_H_
|
||||
#ifndef WEBRTC_VIDEO_ENGINE_TEST_AUTO_TEST_PRIMITIVES_GENERAL_PRIMITIVES_H_
|
||||
#define WEBRTC_VIDEO_ENGINE_TEST_AUTO_TEST_PRIMITIVES_GENERAL_PRIMITIVES_H_
|
||||
|
||||
class ViEToFileRenderer;
|
||||
|
||||
@ -25,6 +25,10 @@ class ViERTP_RTCP;
|
||||
struct VideoCodec;
|
||||
}
|
||||
|
||||
// This constant can be used as input to various functions to not force the
|
||||
// codec resolution.
|
||||
const int kDoNotForceResolution = 0;
|
||||
|
||||
// Finds a suitable capture device (e.g. camera) on the current system
|
||||
// and allocates it. Details about the found device are filled into the out
|
||||
// parameters. If this operation fails, device_id is assigned a negative value
|
||||
@ -69,4 +73,11 @@ bool FindSpecificCodec(webrtc::VideoCodecType of_type,
|
||||
webrtc::ViECodec* codec_interface,
|
||||
webrtc::VideoCodec* result);
|
||||
|
||||
#endif // SRC_VIDEO_ENGINE_MAIN_TEST_AUTOTEST_PRIMITIVES_GENERAL_PRIMITIVES_H_
|
||||
// Sets up the provided codec with a resolution that takes individual codec
|
||||
// quirks into account (except if the forced* variables are
|
||||
// != kDoNotForceResolution)
|
||||
void SetSuitableResolution(webrtc::VideoCodec* video_codec,
|
||||
int forced_codec_width,
|
||||
int forced_codec_height);
|
||||
|
||||
#endif // WEBRTC_VIDEO_ENGINE_TEST_AUTO_TEST_PRIMITIVES_GENERAL_PRIMITIVES_H_
|
||||
|
@ -8,17 +8,18 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "vie_comparison_tests.h"
|
||||
#include "vie_file_based_comparison_tests.h"
|
||||
|
||||
#include "base_primitives.h"
|
||||
#include "codec_primitives.h"
|
||||
#include "framedrop_primitives.h"
|
||||
#include "general_primitives.h"
|
||||
#include "tb_interfaces.h"
|
||||
#include "vie_autotest_defines.h"
|
||||
#include "vie_fake_camera.h"
|
||||
#include "vie_to_file_renderer.h"
|
||||
|
||||
bool ViEComparisonTests::TestCallSetup(
|
||||
bool ViEFileBasedComparisonTests::TestCallSetup(
|
||||
const std::string& i420_video_file,
|
||||
int width,
|
||||
int height,
|
||||
@ -80,7 +81,7 @@ bool ViEComparisonTests::TestCallSetup(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ViEComparisonTests::TestCodecs(
|
||||
bool ViEFileBasedComparisonTests::TestCodecs(
|
||||
const std::string& i420_video_file,
|
||||
int width,
|
||||
int height,
|
||||
@ -111,9 +112,39 @@ bool ViEComparisonTests::TestCodecs(
|
||||
|
||||
// Force the codec resolution to what our input video is so we can make
|
||||
// comparisons later. Our comparison algorithms wouldn't like scaling.
|
||||
::TestCodecs(interfaces, fake_camera.capture_id(), video_channel,
|
||||
width, height);
|
||||
::TestCodecs(interfaces, capture_id, video_channel, width, height);
|
||||
|
||||
fake_camera.StopCamera();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ViEFileBasedComparisonTests::TestFullStack(
|
||||
const std::string& i420_video_file,
|
||||
int width,
|
||||
int height,
|
||||
int bit_rate_kbps,
|
||||
ViEToFileRenderer* local_file_renderer,
|
||||
ViEToFileRenderer* remote_file_renderer,
|
||||
FrameDropDetector* frame_drop_detector) {
|
||||
TbInterfaces interfaces = TbInterfaces("TestFullStack");
|
||||
|
||||
ViEFakeCamera fake_camera(interfaces.capture);
|
||||
if (!fake_camera.StartCameraInNewThread(i420_video_file, width, height)) {
|
||||
// No point in continuing if we have no proper video source
|
||||
ADD_FAILURE() << "Could not open input video " << i420_video_file <<
|
||||
": aborting test...";
|
||||
return;
|
||||
}
|
||||
int video_channel = -1;
|
||||
int capture_id = fake_camera.capture_id();
|
||||
EXPECT_EQ(0, interfaces.base->CreateChannel(video_channel));
|
||||
EXPECT_EQ(0, interfaces.capture->ConnectCaptureDevice(
|
||||
capture_id, video_channel));
|
||||
ConfigureRtpRtcp(interfaces.rtp_rtcp, video_channel);
|
||||
RenderToFile(interfaces.render, capture_id, local_file_renderer);
|
||||
RenderToFile(interfaces.render, video_channel, remote_file_renderer);
|
||||
|
||||
::TestFullStack(interfaces, capture_id, video_channel, width, height,
|
||||
bit_rate_kbps, frame_drop_detector);
|
||||
fake_camera.StopCamera();
|
||||
}
|
@ -44,6 +44,7 @@
|
||||
'interface/vie_autotest_main.h',
|
||||
'interface/vie_autotest_window_manager_interface.h',
|
||||
'interface/vie_autotest_windows.h',
|
||||
'interface/vie_file_based_comparison_tests.h',
|
||||
'interface/vie_window_manager_factory.h',
|
||||
|
||||
# Helper classes
|
||||
@ -58,17 +59,19 @@
|
||||
|
||||
# New, fully automated tests
|
||||
'automated/vie_api_integration_test.cc',
|
||||
'automated/vie_comparison_test.cc',
|
||||
'automated/vie_extended_integration_test.cc',
|
||||
'automated/vie_integration_test_base.cc',
|
||||
'automated/vie_integration_test_base.h',
|
||||
'automated/vie_standard_integration_test.cc',
|
||||
'automated/vie_video_verification_test.cc',
|
||||
|
||||
# Test primitives
|
||||
'primitives/base_primitives.cc',
|
||||
'primitives/base_primitives.h',
|
||||
'primitives/codec_primitives.cc',
|
||||
'primitives/codec_primitives.h',
|
||||
'primitives/framedrop_primitives.h',
|
||||
'primitives/framedrop_primitives.cc',
|
||||
'primitives/general_primitives.cc',
|
||||
'primitives/general_primitives.h',
|
||||
|
||||
@ -92,7 +95,7 @@
|
||||
'source/vie_autotest_rtp_rtcp.cc',
|
||||
'source/vie_autotest_custom_call.cc',
|
||||
'source/vie_autotest_simulcast.cc',
|
||||
'source/vie_comparison_tests.cc',
|
||||
'source/vie_file_based_comparison_tests.cc',
|
||||
|
||||
# Platform dependent
|
||||
# Linux
|
||||
|
@ -60,7 +60,7 @@ void FrameReaderImpl::Close() {
|
||||
bool FrameReaderImpl::ReadFrame(WebRtc_UWord8* source_buffer) {
|
||||
assert(source_buffer);
|
||||
if (input_file_ == NULL) {
|
||||
fprintf(stderr, "FileHandler is not initialized (input file is NULL)\n");
|
||||
fprintf(stderr, "FrameReader is not initialized (input file is NULL)\n");
|
||||
return false;
|
||||
}
|
||||
size_t nbr_read = fread(source_buffer, 1, frame_length_in_bytes_,
|
||||
|
@ -51,7 +51,7 @@ void FrameWriterImpl::Close() {
|
||||
bool FrameWriterImpl::WriteFrame(WebRtc_UWord8* frame_buffer) {
|
||||
assert(frame_buffer);
|
||||
if (output_file_ == NULL) {
|
||||
fprintf(stderr, "FileHandler is not initialized (output file is NULL)\n");
|
||||
fprintf(stderr, "FrameWriter is not initialized (output file is NULL)\n");
|
||||
return false;
|
||||
}
|
||||
int bytes_written = fwrite(frame_buffer, 1, frame_length_in_bytes_,
|
||||
|
Loading…
x
Reference in New Issue
Block a user