aboutsummaryrefslogtreecommitdiff
path: root/webrtc/modules/video_processing/main
diff options
context:
space:
mode:
authorChih-hung Hsieh <chh@google.com>2015-12-01 17:07:48 +0000
committerandroid-build-merger <android-build-merger@google.com>2015-12-01 17:07:48 +0000
commita4acd9d6bc9b3b033d7d274316e75ee067df8d20 (patch)
tree672a185b294789cf991f385c3e395dd63bea9063 /webrtc/modules/video_processing/main
parent3681b90ba4fe7a27232dd3e27897d5d7ed9d651c (diff)
parentfe8b4a657979b49e1701bd92f6d5814a99e0b2be (diff)
downloadwebrtc-a4acd9d6bc9b3b033d7d274316e75ee067df8d20.tar.gz
Merge changes I7bbf776e,I1b827825
am: fe8b4a6579 * commit 'fe8b4a657979b49e1701bd92f6d5814a99e0b2be': (7237 commits) WIP: Changes after merge commit 'cb3f9bd' Make the nonlinear beamformer steerable Utilize bitrate above codec max to protect video. Enable VP9 internal resize by default. Filter overlapping RTP header extensions. Make VCMEncodedFrameCallback const. MediaCodecVideoEncoder: Add number of quality resolution downscales to Encoded callback. Remove redudant encoder rate calls. Create isolate files for nonparallel tests. Register header extensions in RtpRtcpObserver to avoid log spam. Make an enum class out of NetEqDecoder, and hide the neteq_decoders_ table ACM: Move NACK functionality inside NetEq Fix chromium-style warnings in webrtc/sound/. Create a 'webrtc_nonparallel_tests' target. Update scalability structure data according to updates in the RTP payload profile. audio_coding: rename interface -> include Rewrote perform_action_on_all_files to be parallell. Update reference indices according to updates in the RTP payload profile. Disable P2PTransport...TestFailoverControlledSide on Memcheck pass clangcl compile options to ignore warnings in gflags.cc ...
Diffstat (limited to 'webrtc/modules/video_processing/main')
-rw-r--r--webrtc/modules/video_processing/main/interface/video_processing.h270
-rw-r--r--webrtc/modules/video_processing/main/interface/video_processing_defines.h41
-rw-r--r--webrtc/modules/video_processing/main/source/OWNERS5
-rw-r--r--webrtc/modules/video_processing/main/source/brighten.cc45
-rw-r--r--webrtc/modules/video_processing/main/source/brighten.h25
-rw-r--r--webrtc/modules/video_processing/main/source/brightness_detection.cc133
-rw-r--r--webrtc/modules/video_processing/main/source/brightness_detection.h37
-rw-r--r--webrtc/modules/video_processing/main/source/content_analysis.cc274
-rw-r--r--webrtc/modules/video_processing/main/source/content_analysis.h87
-rw-r--r--webrtc/modules/video_processing/main/source/content_analysis_sse2.cc264
-rw-r--r--webrtc/modules/video_processing/main/source/deflickering.cc398
-rw-r--r--webrtc/modules/video_processing/main/source/deflickering.h56
-rw-r--r--webrtc/modules/video_processing/main/source/frame_preprocessor.cc136
-rw-r--r--webrtc/modules/video_processing/main/source/frame_preprocessor.h79
-rw-r--r--webrtc/modules/video_processing/main/source/spatial_resampler.cc98
-rw-r--r--webrtc/modules/video_processing/main/source/spatial_resampler.h61
-rw-r--r--webrtc/modules/video_processing/main/source/video_decimator.cc146
-rw-r--r--webrtc/modules/video_processing/main/source/video_decimator.h58
-rw-r--r--webrtc/modules/video_processing/main/source/video_processing_impl.cc183
-rw-r--r--webrtc/modules/video_processing/main/source/video_processing_impl.h75
-rw-r--r--webrtc/modules/video_processing/main/test/unit_test/brightness_detection_test.cc121
-rw-r--r--webrtc/modules/video_processing/main/test/unit_test/content_metrics_test.cc44
-rw-r--r--webrtc/modules/video_processing/main/test/unit_test/createTable.m179
-rw-r--r--webrtc/modules/video_processing/main/test/unit_test/deflickering_test.cc100
-rw-r--r--webrtc/modules/video_processing/main/test/unit_test/readYUV420file.m45
-rw-r--r--webrtc/modules/video_processing/main/test/unit_test/video_processing_unittest.cc390
-rw-r--r--webrtc/modules/video_processing/main/test/unit_test/video_processing_unittest.h47
-rw-r--r--webrtc/modules/video_processing/main/test/unit_test/writeYUV420file.m22
28 files changed, 3419 insertions, 0 deletions
diff --git a/webrtc/modules/video_processing/main/interface/video_processing.h b/webrtc/modules/video_processing/main/interface/video_processing.h
new file mode 100644
index 0000000000..30af99fb8e
--- /dev/null
+++ b/webrtc/modules/video_processing/main/interface/video_processing.h
@@ -0,0 +1,270 @@
+/*
+ * 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.
+ */
+
+/*
+ * video_processing.h
+ * This header file contains the API required for the video
+ * processing module class.
+ */
+
+
+#ifndef WEBRTC_MODULES_INTERFACE_VIDEO_PROCESSING_H
+#define WEBRTC_MODULES_INTERFACE_VIDEO_PROCESSING_H
+
+#include "webrtc/modules/interface/module.h"
+#include "webrtc/modules/interface/module_common_types.h"
+#include "webrtc/modules/video_processing/main/interface/video_processing_defines.h"
+#include "webrtc/video_frame.h"
+
+/**
+ The module is largely intended to process video streams, except functionality
+ provided by static functions which operate independent of previous frames. It
+ is recommended, but not required that a unique instance be used for each
+ concurrently processed stream. Similarly, it is recommended to call Reset()
+ before switching to a new stream, but this is not absolutely required.
+
+ The module provides basic thread safety by permitting only a single function
+ to execute concurrently.
+*/
+
+namespace webrtc {
+
+class VideoProcessingModule : public Module {
+ public:
+ /**
+ Structure to hold frame statistics. Populate it with GetFrameStats().
+ */
+ struct FrameStats {
+ FrameStats() :
+ mean(0),
+ sum(0),
+ num_pixels(0),
+ subSamplWidth(0),
+ subSamplHeight(0) {
+ memset(hist, 0, sizeof(hist));
+ }
+
+ uint32_t hist[256]; // FRame histogram.
+ uint32_t mean; // Frame Mean value.
+ uint32_t sum; // Sum of frame.
+ uint32_t num_pixels; // Number of pixels.
+ uint8_t subSamplWidth; // Subsampling rate of width in powers of 2.
+ uint8_t subSamplHeight; // Subsampling rate of height in powers of 2.
+};
+
+ /**
+ Specifies the warning types returned by BrightnessDetection().
+ */
+ enum BrightnessWarning {
+ kNoWarning, // Frame has acceptable brightness.
+ kDarkWarning, // Frame is too dark.
+ kBrightWarning // Frame is too bright.
+ };
+
+ /*
+ Creates a VPM object.
+
+ \param[in] id
+ Unique identifier of this object.
+
+ \return Pointer to a VPM object.
+ */
+ static VideoProcessingModule* Create();
+
+ /**
+ Destroys a VPM object.
+
+ \param[in] module
+ Pointer to the VPM object to destroy.
+ */
+ static void Destroy(VideoProcessingModule* module);
+
+ /**
+ Not supported.
+ */
+ int64_t TimeUntilNextProcess() override { return -1; }
+
+ /**
+ Not supported.
+ */
+ int32_t Process() override { return -1; }
+
+ /**
+ Resets all processing components to their initial states. This should be
+ called whenever a new video stream is started.
+ */
+ virtual void Reset() = 0;
+
+ /**
+ Retrieves statistics for the input frame. This function must be used to
+ prepare a FrameStats struct for use in certain VPM functions.
+
+ \param[out] stats
+ The frame statistics will be stored here on return.
+
+ \param[in] frame
+ Reference to the video frame.
+
+ \return 0 on success, -1 on failure.
+ */
+ static int32_t GetFrameStats(FrameStats* stats, const VideoFrame& frame);
+
+ /**
+ Checks the validity of a FrameStats struct. Currently, valid implies only
+ that is had changed from its initialized state.
+
+ \param[in] stats
+ Frame statistics.
+
+ \return True on valid stats, false on invalid stats.
+ */
+ static bool ValidFrameStats(const FrameStats& stats);
+
+ /**
+ Returns a FrameStats struct to its intialized state.
+
+ \param[in,out] stats
+ Frame statistics.
+ */
+ static void ClearFrameStats(FrameStats* stats);
+
+ /**
+ Increases/decreases the luminance value.
+
+ \param[in,out] frame
+ Pointer to the video frame.
+
+ \param[in] delta
+ The amount to change the chrominance value of every single pixel.
+ Can be < 0 also.
+
+ \return 0 on success, -1 on failure.
+ */
+ static int32_t Brighten(VideoFrame* frame, int delta);
+
+ /**
+ Detects and removes camera flicker from a video stream. Every frame from
+ the stream must be passed in. A frame will only be altered if flicker has
+ been detected. Has a fixed-point implementation.
+
+ \param[in,out] frame
+ Pointer to the video frame.
+
+ \param[in,out] stats
+ Frame statistics provided by GetFrameStats(). On return the stats will
+ be reset to zero if the frame was altered. Call GetFrameStats() again
+ if the statistics for the altered frame are required.
+
+ \return 0 on success, -1 on failure.
+ */
+ virtual int32_t Deflickering(VideoFrame* frame, FrameStats* stats) = 0;
+
+ /**
+ Detects if a video frame is excessively bright or dark. Returns a
+ warning if this is the case. Multiple frames should be passed in before
+ expecting a warning. Has a floating-point implementation.
+
+ \param[in] frame
+ Pointer to the video frame.
+
+ \param[in] stats
+ Frame statistics provided by GetFrameStats().
+
+ \return A member of BrightnessWarning on success, -1 on error
+ */
+ virtual int32_t BrightnessDetection(const VideoFrame& frame,
+ const FrameStats& stats) = 0;
+
+ /**
+ The following functions refer to the pre-processor unit within VPM. The
+ pre-processor perfoms spatial/temporal decimation and content analysis on
+ the frames prior to encoding.
+ */
+
+ /**
+ Enable/disable temporal decimation
+
+ \param[in] enable when true, temporal decimation is enabled
+ */
+ virtual void EnableTemporalDecimation(bool enable) = 0;
+
+ /**
+ Set target resolution
+
+ \param[in] width
+ Target width
+
+ \param[in] height
+ Target height
+
+ \param[in] frame_rate
+ Target frame_rate
+
+ \return VPM_OK on success, a negative value on error (see error codes)
+
+ */
+ virtual int32_t SetTargetResolution(uint32_t width,
+ uint32_t height,
+ uint32_t frame_rate) = 0;
+
+ virtual void SetTargetFramerate(int frame_rate) {}
+
+ /**
+ Get decimated(target) frame rate
+ */
+ virtual uint32_t Decimatedframe_rate() = 0;
+
+ /**
+ Get decimated(target) frame width
+ */
+ virtual uint32_t DecimatedWidth() const = 0;
+
+ /**
+ Get decimated(target) frame height
+ */
+ virtual uint32_t DecimatedHeight() const = 0 ;
+
+ /**
+ Set the spatial resampling settings of the VPM: The resampler may either be
+ disabled or one of the following:
+ scaling to a close to target dimension followed by crop/pad
+
+ \param[in] resampling_mode
+ Set resampling mode (a member of VideoFrameResampling)
+ */
+ virtual void SetInputFrameResampleMode(VideoFrameResampling
+ resampling_mode) = 0;
+
+ /**
+ Get Processed (decimated) frame
+
+ \param[in] frame pointer to the video frame.
+ \param[in] processed_frame pointer (double) to the processed frame. If no
+ processing is required, processed_frame will be NULL.
+
+ \return VPM_OK on success, a negative value on error (see error codes)
+ */
+ virtual int32_t PreprocessFrame(const VideoFrame& frame,
+ VideoFrame** processed_frame) = 0;
+
+ /**
+ Return content metrics for the last processed frame
+ */
+ virtual VideoContentMetrics* ContentMetrics() const = 0 ;
+
+ /**
+ Enable content analysis
+ */
+ virtual void EnableContentAnalysis(bool enable) = 0;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_INTERFACE_VIDEO_PROCESSING_H
diff --git a/webrtc/modules/video_processing/main/interface/video_processing_defines.h b/webrtc/modules/video_processing/main/interface/video_processing_defines.h
new file mode 100644
index 0000000000..93a0658966
--- /dev/null
+++ b/webrtc/modules/video_processing/main/interface/video_processing_defines.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+/*
+ * video_processing_defines.h
+ * This header file includes the definitions used in the video processor module
+ */
+
+#ifndef WEBRTC_MODULES_INTERFACE_VIDEO_PROCESSING_DEFINES_H
+#define WEBRTC_MODULES_INTERFACE_VIDEO_PROCESSING_DEFINES_H
+
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+// Error codes
+#define VPM_OK 0
+#define VPM_GENERAL_ERROR -1
+#define VPM_MEMORY -2
+#define VPM_PARAMETER_ERROR -3
+#define VPM_SCALE_ERROR -4
+#define VPM_UNINITIALIZED -5
+#define VPM_UNIMPLEMENTED -6
+
+enum VideoFrameResampling {
+ kNoRescaling, // Disables rescaling.
+ kFastRescaling, // Point filter.
+ kBiLinear, // Bi-linear interpolation.
+ kBox, // Box inteprolation.
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_INTERFACE_VIDEO_PROCESSING_DEFINES_H
diff --git a/webrtc/modules/video_processing/main/source/OWNERS b/webrtc/modules/video_processing/main/source/OWNERS
new file mode 100644
index 0000000000..3ee6b4bf5f
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/OWNERS
@@ -0,0 +1,5 @@
+
+# These are for the common case of adding or renaming files. If you're doing
+# structural changes, please get a review from a reviewer in this file.
+per-file *.gyp=*
+per-file *.gypi=*
diff --git a/webrtc/modules/video_processing/main/source/brighten.cc b/webrtc/modules/video_processing/main/source/brighten.cc
new file mode 100644
index 0000000000..1fe813e7b0
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/brighten.cc
@@ -0,0 +1,45 @@
+/*
+ * 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 "webrtc/modules/video_processing/main/source/brighten.h"
+
+#include <stdlib.h>
+
+namespace webrtc {
+namespace VideoProcessing {
+
+int32_t Brighten(VideoFrame* frame, int delta) {
+ assert(frame);
+ if (frame->IsZeroSize()) {
+ return VPM_PARAMETER_ERROR;
+ }
+ if (frame->width() <= 0 || frame->height() <= 0) {
+ return VPM_PARAMETER_ERROR;
+ }
+
+ int num_pixels = frame->width() * frame->height();
+
+ int look_up[256];
+ for (int i = 0; i < 256; i++) {
+ int val = i + delta;
+ look_up[i] = ((((val < 0) ? 0 : val) > 255) ? 255 : val);
+ }
+
+ uint8_t* temp_ptr = frame->buffer(kYPlane);
+
+ for (int i = 0; i < num_pixels; i++) {
+ *temp_ptr = static_cast<uint8_t>(look_up[*temp_ptr]);
+ temp_ptr++;
+ }
+ return VPM_OK;
+}
+
+} // namespace VideoProcessing
+} // namespace webrtc
diff --git a/webrtc/modules/video_processing/main/source/brighten.h b/webrtc/modules/video_processing/main/source/brighten.h
new file mode 100644
index 0000000000..151d7a3b51
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/brighten.h
@@ -0,0 +1,25 @@
+/*
+ * 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 MODULES_VIDEO_PROCESSING_MAIN_SOURCE_BRIGHTEN_H_
+#define MODULES_VIDEO_PROCESSING_MAIN_SOURCE_BRIGHTEN_H_
+
+#include "webrtc/modules/video_processing/main/interface/video_processing.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+namespace VideoProcessing {
+
+int32_t Brighten(VideoFrame* frame, int delta);
+
+} // namespace VideoProcessing
+} // namespace webrtc
+
+#endif // MODULES_VIDEO_PROCESSING_MAIN_SOURCE_BRIGHTEN_H_
diff --git a/webrtc/modules/video_processing/main/source/brightness_detection.cc b/webrtc/modules/video_processing/main/source/brightness_detection.cc
new file mode 100644
index 0000000000..bae225b3b0
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/brightness_detection.cc
@@ -0,0 +1,133 @@
+/*
+ * 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 "webrtc/modules/video_processing/main/interface/video_processing.h"
+#include "webrtc/modules/video_processing/main/source/brightness_detection.h"
+
+#include <math.h>
+
+namespace webrtc {
+
+VPMBrightnessDetection::VPMBrightnessDetection() {
+ Reset();
+}
+
+VPMBrightnessDetection::~VPMBrightnessDetection() {}
+
+void VPMBrightnessDetection::Reset() {
+ frame_cnt_bright_ = 0;
+ frame_cnt_dark_ = 0;
+}
+
+int32_t VPMBrightnessDetection::ProcessFrame(
+ const VideoFrame& frame,
+ const VideoProcessingModule::FrameStats& stats) {
+ if (frame.IsZeroSize()) {
+ return VPM_PARAMETER_ERROR;
+ }
+ int width = frame.width();
+ int height = frame.height();
+
+ if (!VideoProcessingModule::ValidFrameStats(stats)) {
+ return VPM_PARAMETER_ERROR;
+ }
+
+ const uint8_t frame_cnt_alarm = 2;
+
+ // Get proportion in lowest bins.
+ uint8_t low_th = 20;
+ float prop_low = 0;
+ for (uint32_t i = 0; i < low_th; i++) {
+ prop_low += stats.hist[i];
+ }
+ prop_low /= stats.num_pixels;
+
+ // Get proportion in highest bins.
+ unsigned char high_th = 230;
+ float prop_high = 0;
+ for (uint32_t i = high_th; i < 256; i++) {
+ prop_high += stats.hist[i];
+ }
+ prop_high /= stats.num_pixels;
+
+ if (prop_high < 0.4) {
+ if (stats.mean < 90 || stats.mean > 170) {
+ // Standard deviation of Y
+ const uint8_t* buffer = frame.buffer(kYPlane);
+ float std_y = 0;
+ for (int h = 0; h < height; h += (1 << stats.subSamplHeight)) {
+ int row = h*width;
+ for (int w = 0; w < width; w += (1 << stats.subSamplWidth)) {
+ std_y += (buffer[w + row] - stats.mean) * (buffer[w + row] -
+ stats.mean);
+ }
+ }
+ std_y = sqrt(std_y / stats.num_pixels);
+
+ // Get percentiles.
+ uint32_t sum = 0;
+ uint32_t median_y = 140;
+ uint32_t perc05 = 0;
+ uint32_t perc95 = 255;
+ float pos_perc05 = stats.num_pixels * 0.05f;
+ float pos_median = stats.num_pixels * 0.5f;
+ float posPerc95 = stats.num_pixels * 0.95f;
+ for (uint32_t i = 0; i < 256; i++) {
+ sum += stats.hist[i];
+ if (sum < pos_perc05) perc05 = i; // 5th perc.
+ if (sum < pos_median) median_y = i; // 50th perc.
+ if (sum < posPerc95)
+ perc95 = i; // 95th perc.
+ else
+ break;
+ }
+
+ // Check if image is too dark
+ if ((std_y < 55) && (perc05 < 50)) {
+ if (median_y < 60 || stats.mean < 80 || perc95 < 130 ||
+ prop_low > 0.20) {
+ frame_cnt_dark_++;
+ } else {
+ frame_cnt_dark_ = 0;
+ }
+ } else {
+ frame_cnt_dark_ = 0;
+ }
+
+ // Check if image is too bright
+ if ((std_y < 52) && (perc95 > 200) && (median_y > 160)) {
+ if (median_y > 185 || stats.mean > 185 || perc05 > 140 ||
+ prop_high > 0.25) {
+ frame_cnt_bright_++;
+ } else {
+ frame_cnt_bright_ = 0;
+ }
+ } else {
+ frame_cnt_bright_ = 0;
+ }
+ } else {
+ frame_cnt_dark_ = 0;
+ frame_cnt_bright_ = 0;
+ }
+ } else {
+ frame_cnt_bright_++;
+ frame_cnt_dark_ = 0;
+ }
+
+ if (frame_cnt_dark_ > frame_cnt_alarm) {
+ return VideoProcessingModule::kDarkWarning;
+ } else if (frame_cnt_bright_ > frame_cnt_alarm) {
+ return VideoProcessingModule::kBrightWarning;
+ } else {
+ return VideoProcessingModule::kNoWarning;
+ }
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/video_processing/main/source/brightness_detection.h b/webrtc/modules/video_processing/main/source/brightness_detection.h
new file mode 100644
index 0000000000..48532b4a20
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/brightness_detection.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+/*
+ * brightness_detection.h
+ */
+#ifndef MODULES_VIDEO_PROCESSING_MAIN_SOURCE_BRIGHTNESS_DETECTION_H
+#define MODULES_VIDEO_PROCESSING_MAIN_SOURCE_BRIGHTNESS_DETECTION_H
+#include "webrtc/modules/video_processing/main/interface/video_processing.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+class VPMBrightnessDetection {
+ public:
+ VPMBrightnessDetection();
+ ~VPMBrightnessDetection();
+
+ void Reset();
+ int32_t ProcessFrame(const VideoFrame& frame,
+ const VideoProcessingModule::FrameStats& stats);
+
+ private:
+ uint32_t frame_cnt_bright_;
+ uint32_t frame_cnt_dark_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_VIDEO_PROCESSING_MAIN_SOURCE_BRIGHTNESS_DETECTION_H
diff --git a/webrtc/modules/video_processing/main/source/content_analysis.cc b/webrtc/modules/video_processing/main/source/content_analysis.cc
new file mode 100644
index 0000000000..d29db27408
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/content_analysis.cc
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#include "webrtc/modules/video_processing/main/source/content_analysis.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "webrtc/system_wrappers/include/cpu_features_wrapper.h"
+#include "webrtc/system_wrappers/include/tick_util.h"
+
+namespace webrtc {
+
+VPMContentAnalysis::VPMContentAnalysis(bool runtime_cpu_detection)
+ : orig_frame_(NULL),
+ prev_frame_(NULL),
+ width_(0),
+ height_(0),
+ skip_num_(1),
+ border_(8),
+ motion_magnitude_(0.0f),
+ spatial_pred_err_(0.0f),
+ spatial_pred_err_h_(0.0f),
+ spatial_pred_err_v_(0.0f),
+ first_frame_(true),
+ ca_Init_(false),
+ content_metrics_(NULL) {
+ ComputeSpatialMetrics = &VPMContentAnalysis::ComputeSpatialMetrics_C;
+ TemporalDiffMetric = &VPMContentAnalysis::TemporalDiffMetric_C;
+
+ if (runtime_cpu_detection) {
+#if defined(WEBRTC_ARCH_X86_FAMILY)
+ if (WebRtc_GetCPUInfo(kSSE2)) {
+ ComputeSpatialMetrics = &VPMContentAnalysis::ComputeSpatialMetrics_SSE2;
+ TemporalDiffMetric = &VPMContentAnalysis::TemporalDiffMetric_SSE2;
+ }
+#endif
+ }
+ Release();
+}
+
+VPMContentAnalysis::~VPMContentAnalysis() {
+ Release();
+}
+
+VideoContentMetrics* VPMContentAnalysis::ComputeContentMetrics(
+ const VideoFrame& inputFrame) {
+ if (inputFrame.IsZeroSize())
+ return NULL;
+
+ // Init if needed (native dimension change).
+ if (width_ != inputFrame.width() || height_ != inputFrame.height()) {
+ if (VPM_OK != Initialize(inputFrame.width(), inputFrame.height()))
+ return NULL;
+ }
+ // Only interested in the Y plane.
+ orig_frame_ = inputFrame.buffer(kYPlane);
+
+ // Compute spatial metrics: 3 spatial prediction errors.
+ (this->*ComputeSpatialMetrics)();
+
+ // Compute motion metrics
+ if (first_frame_ == false)
+ ComputeMotionMetrics();
+
+ // Saving current frame as previous one: Y only.
+ memcpy(prev_frame_, orig_frame_, width_ * height_);
+
+ first_frame_ = false;
+ ca_Init_ = true;
+
+ return ContentMetrics();
+}
+
+int32_t VPMContentAnalysis::Release() {
+ if (content_metrics_ != NULL) {
+ delete content_metrics_;
+ content_metrics_ = NULL;
+ }
+
+ if (prev_frame_ != NULL) {
+ delete [] prev_frame_;
+ prev_frame_ = NULL;
+ }
+
+ width_ = 0;
+ height_ = 0;
+ first_frame_ = true;
+
+ return VPM_OK;
+}
+
+int32_t VPMContentAnalysis::Initialize(int width, int height) {
+ width_ = width;
+ height_ = height;
+ first_frame_ = true;
+
+ // skip parameter: # of skipped rows: for complexity reduction
+ // temporal also currently uses it for column reduction.
+ skip_num_ = 1;
+
+ // use skipNum = 2 for 4CIF, WHD
+ if ( (height_ >= 576) && (width_ >= 704) ) {
+ skip_num_ = 2;
+ }
+ // use skipNum = 4 for FULLL_HD images
+ if ( (height_ >= 1080) && (width_ >= 1920) ) {
+ skip_num_ = 4;
+ }
+
+ if (content_metrics_ != NULL) {
+ delete content_metrics_;
+ }
+
+ if (prev_frame_ != NULL) {
+ delete [] prev_frame_;
+ }
+
+ // Spatial Metrics don't work on a border of 8. Minimum processing
+ // block size is 16 pixels. So make sure the width and height support this.
+ if (width_ <= 32 || height_ <= 32) {
+ ca_Init_ = false;
+ return VPM_PARAMETER_ERROR;
+ }
+
+ content_metrics_ = new VideoContentMetrics();
+ if (content_metrics_ == NULL) {
+ return VPM_MEMORY;
+ }
+
+ prev_frame_ = new uint8_t[width_ * height_]; // Y only.
+ if (prev_frame_ == NULL) return VPM_MEMORY;
+
+ return VPM_OK;
+}
+
+
+// Compute motion metrics: magnitude over non-zero motion vectors,
+// and size of zero cluster
+int32_t VPMContentAnalysis::ComputeMotionMetrics() {
+ // Motion metrics: only one is derived from normalized
+ // (MAD) temporal difference
+ (this->*TemporalDiffMetric)();
+ return VPM_OK;
+}
+
+// Normalized temporal difference (MAD): used as a motion level metric
+// Normalize MAD by spatial contrast: images with more contrast
+// (pixel variance) likely have larger temporal difference
+// To reduce complexity, we compute the metric for a reduced set of points.
+int32_t VPMContentAnalysis::TemporalDiffMetric_C() {
+ // size of original frame
+ int sizei = height_;
+ int sizej = width_;
+ uint32_t tempDiffSum = 0;
+ uint32_t pixelSum = 0;
+ uint64_t pixelSqSum = 0;
+
+ uint32_t num_pixels = 0; // Counter for # of pixels.
+ const int width_end = ((width_ - 2*border_) & -16) + border_;
+
+ for (int i = border_; i < sizei - border_; i += skip_num_) {
+ for (int j = border_; j < width_end; j++) {
+ num_pixels += 1;
+ int ssn = i * sizej + j;
+
+ uint8_t currPixel = orig_frame_[ssn];
+ uint8_t prevPixel = prev_frame_[ssn];
+
+ tempDiffSum += (uint32_t)abs((int16_t)(currPixel - prevPixel));
+ pixelSum += (uint32_t) currPixel;
+ pixelSqSum += (uint64_t) (currPixel * currPixel);
+ }
+ }
+
+ // Default.
+ motion_magnitude_ = 0.0f;
+
+ if (tempDiffSum == 0) return VPM_OK;
+
+ // Normalize over all pixels.
+ float const tempDiffAvg = (float)tempDiffSum / (float)(num_pixels);
+ float const pixelSumAvg = (float)pixelSum / (float)(num_pixels);
+ float const pixelSqSumAvg = (float)pixelSqSum / (float)(num_pixels);
+ float contrast = pixelSqSumAvg - (pixelSumAvg * pixelSumAvg);
+
+ if (contrast > 0.0) {
+ contrast = sqrt(contrast);
+ motion_magnitude_ = tempDiffAvg/contrast;
+ }
+ return VPM_OK;
+}
+
+// Compute spatial metrics:
+// To reduce complexity, we compute the metric for a reduced set of points.
+// The spatial metrics are rough estimates of the prediction error cost for
+// each QM spatial mode: 2x2,1x2,2x1
+// The metrics are a simple estimate of the up-sampling prediction error,
+// estimated assuming sub-sampling for decimation (no filtering),
+// and up-sampling back up with simple bilinear interpolation.
+int32_t VPMContentAnalysis::ComputeSpatialMetrics_C() {
+ const int sizei = height_;
+ const int sizej = width_;
+
+ // Pixel mean square average: used to normalize the spatial metrics.
+ uint32_t pixelMSA = 0;
+
+ uint32_t spatialErrSum = 0;
+ uint32_t spatialErrVSum = 0;
+ uint32_t spatialErrHSum = 0;
+
+ // make sure work section is a multiple of 16
+ const int width_end = ((sizej - 2*border_) & -16) + border_;
+
+ for (int i = border_; i < sizei - border_; i += skip_num_) {
+ for (int j = border_; j < width_end; j++) {
+ int ssn1= i * sizej + j;
+ int ssn2 = (i + 1) * sizej + j; // bottom
+ int ssn3 = (i - 1) * sizej + j; // top
+ int ssn4 = i * sizej + j + 1; // right
+ int ssn5 = i * sizej + j - 1; // left
+
+ uint16_t refPixel1 = orig_frame_[ssn1] << 1;
+ uint16_t refPixel2 = orig_frame_[ssn1] << 2;
+
+ uint8_t bottPixel = orig_frame_[ssn2];
+ uint8_t topPixel = orig_frame_[ssn3];
+ uint8_t rightPixel = orig_frame_[ssn4];
+ uint8_t leftPixel = orig_frame_[ssn5];
+
+ spatialErrSum += (uint32_t) abs((int16_t)(refPixel2
+ - (uint16_t)(bottPixel + topPixel + leftPixel + rightPixel)));
+ spatialErrVSum += (uint32_t) abs((int16_t)(refPixel1
+ - (uint16_t)(bottPixel + topPixel)));
+ spatialErrHSum += (uint32_t) abs((int16_t)(refPixel1
+ - (uint16_t)(leftPixel + rightPixel)));
+ pixelMSA += orig_frame_[ssn1];
+ }
+ }
+
+ // Normalize over all pixels.
+ const float spatialErr = (float)(spatialErrSum >> 2);
+ const float spatialErrH = (float)(spatialErrHSum >> 1);
+ const float spatialErrV = (float)(spatialErrVSum >> 1);
+ const float norm = (float)pixelMSA;
+
+ // 2X2:
+ spatial_pred_err_ = spatialErr / norm;
+ // 1X2:
+ spatial_pred_err_h_ = spatialErrH / norm;
+ // 2X1:
+ spatial_pred_err_v_ = spatialErrV / norm;
+ return VPM_OK;
+}
+
+VideoContentMetrics* VPMContentAnalysis::ContentMetrics() {
+ if (ca_Init_ == false) return NULL;
+
+ content_metrics_->spatial_pred_err = spatial_pred_err_;
+ content_metrics_->spatial_pred_err_h = spatial_pred_err_h_;
+ content_metrics_->spatial_pred_err_v = spatial_pred_err_v_;
+ // Motion metric: normalized temporal difference (MAD).
+ content_metrics_->motion_magnitude = motion_magnitude_;
+
+ return content_metrics_;
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/video_processing/main/source/content_analysis.h b/webrtc/modules/video_processing/main/source/content_analysis.h
new file mode 100644
index 0000000000..510c1b4a55
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/content_analysis.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_CONTENT_ANALYSIS_H
+#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_CONTENT_ANALYSIS_H
+
+#include "webrtc/modules/interface/module_common_types.h"
+#include "webrtc/modules/video_processing/main/interface/video_processing_defines.h"
+#include "webrtc/typedefs.h"
+#include "webrtc/video_frame.h"
+
+namespace webrtc {
+
+class VPMContentAnalysis {
+ public:
+ // When |runtime_cpu_detection| is true, runtime selection of an optimized
+ // code path is allowed.
+ explicit VPMContentAnalysis(bool runtime_cpu_detection);
+ ~VPMContentAnalysis();
+
+ // Initialize ContentAnalysis - should be called prior to
+ // extractContentFeature
+ // Inputs: width, height
+ // Return value: 0 if OK, negative value upon error
+ int32_t Initialize(int width, int height);
+
+ // Extract content Feature - main function of ContentAnalysis
+ // Input: new frame
+ // Return value: pointer to structure containing content Analysis
+ // metrics or NULL value upon error
+ VideoContentMetrics* ComputeContentMetrics(const VideoFrame& inputFrame);
+
+ // Release all allocated memory
+ // Output: 0 if OK, negative value upon error
+ int32_t Release();
+
+ private:
+ // return motion metrics
+ VideoContentMetrics* ContentMetrics();
+
+ // Normalized temporal difference metric: for motion magnitude
+ typedef int32_t (VPMContentAnalysis::*TemporalDiffMetricFunc)();
+ TemporalDiffMetricFunc TemporalDiffMetric;
+ int32_t TemporalDiffMetric_C();
+
+ // Motion metric method: call 2 metrics (magnitude and size)
+ int32_t ComputeMotionMetrics();
+
+ // Spatial metric method: computes the 3 frame-average spatial
+ // prediction errors (1x2,2x1,2x2)
+ typedef int32_t (VPMContentAnalysis::*ComputeSpatialMetricsFunc)();
+ ComputeSpatialMetricsFunc ComputeSpatialMetrics;
+ int32_t ComputeSpatialMetrics_C();
+
+#if defined(WEBRTC_ARCH_X86_FAMILY)
+ int32_t ComputeSpatialMetrics_SSE2();
+ int32_t TemporalDiffMetric_SSE2();
+#endif
+
+ const uint8_t* orig_frame_;
+ uint8_t* prev_frame_;
+ int width_;
+ int height_;
+ int skip_num_;
+ int border_;
+
+ // Content Metrics: Stores the local average of the metrics.
+ float motion_magnitude_; // motion class
+ float spatial_pred_err_; // spatial class
+ float spatial_pred_err_h_; // spatial class
+ float spatial_pred_err_v_; // spatial class
+ bool first_frame_;
+ bool ca_Init_;
+
+ VideoContentMetrics* content_metrics_;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_CONTENT_ANALYSIS_H
diff --git a/webrtc/modules/video_processing/main/source/content_analysis_sse2.cc b/webrtc/modules/video_processing/main/source/content_analysis_sse2.cc
new file mode 100644
index 0000000000..17b64ff280
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/content_analysis_sse2.cc
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/video_processing/main/source/content_analysis.h"
+
+#include <emmintrin.h>
+#include <math.h>
+
+namespace webrtc {
+
+int32_t VPMContentAnalysis::TemporalDiffMetric_SSE2() {
+ uint32_t num_pixels = 0; // counter for # of pixels
+ const uint8_t* imgBufO = orig_frame_ + border_*width_ + border_;
+ const uint8_t* imgBufP = prev_frame_ + border_*width_ + border_;
+
+ const int32_t width_end = ((width_ - 2*border_) & -16) + border_;
+
+ __m128i sad_64 = _mm_setzero_si128();
+ __m128i sum_64 = _mm_setzero_si128();
+ __m128i sqsum_64 = _mm_setzero_si128();
+ const __m128i z = _mm_setzero_si128();
+
+ for (uint16_t i = 0; i < (height_ - 2*border_); i += skip_num_) {
+ __m128i sqsum_32 = _mm_setzero_si128();
+
+ const uint8_t *lineO = imgBufO;
+ const uint8_t *lineP = imgBufP;
+
+ // Work on 16 pixels at a time. For HD content with a width of 1920
+ // this loop will run ~67 times (depending on border). Maximum for
+ // abs(o-p) and sum(o) will be 255. _mm_sad_epu8 produces 2 64 bit
+ // results which are then accumulated. There is no chance of
+ // rollover for these two accumulators.
+ // o*o will have a maximum of 255*255 = 65025. This will roll over
+ // a 16 bit accumulator as 67*65025 > 65535, but will fit in a
+ // 32 bit accumulator.
+ for (uint16_t j = 0; j < width_end - border_; j += 16) {
+ const __m128i o = _mm_loadu_si128((__m128i*)(lineO));
+ const __m128i p = _mm_loadu_si128((__m128i*)(lineP));
+
+ lineO += 16;
+ lineP += 16;
+
+ // Abs pixel difference between frames.
+ sad_64 = _mm_add_epi64 (sad_64, _mm_sad_epu8(o, p));
+
+ // sum of all pixels in frame
+ sum_64 = _mm_add_epi64 (sum_64, _mm_sad_epu8(o, z));
+
+ // Squared sum of all pixels in frame.
+ const __m128i olo = _mm_unpacklo_epi8(o,z);
+ const __m128i ohi = _mm_unpackhi_epi8(o,z);
+
+ const __m128i sqsum_32_lo = _mm_madd_epi16(olo, olo);
+ const __m128i sqsum_32_hi = _mm_madd_epi16(ohi, ohi);
+
+ sqsum_32 = _mm_add_epi32(sqsum_32, sqsum_32_lo);
+ sqsum_32 = _mm_add_epi32(sqsum_32, sqsum_32_hi);
+ }
+
+ // Add to 64 bit running sum as to not roll over.
+ sqsum_64 = _mm_add_epi64(sqsum_64,
+ _mm_add_epi64(_mm_unpackhi_epi32(sqsum_32,z),
+ _mm_unpacklo_epi32(sqsum_32,z)));
+
+ imgBufO += width_ * skip_num_;
+ imgBufP += width_ * skip_num_;
+ num_pixels += (width_end - border_);
+ }
+
+ __m128i sad_final_128;
+ __m128i sum_final_128;
+ __m128i sqsum_final_128;
+
+ // Bring sums out of vector registers and into integer register
+ // domain, summing them along the way.
+ _mm_store_si128 (&sad_final_128, sad_64);
+ _mm_store_si128 (&sum_final_128, sum_64);
+ _mm_store_si128 (&sqsum_final_128, sqsum_64);
+
+ uint64_t *sad_final_64 = reinterpret_cast<uint64_t*>(&sad_final_128);
+ uint64_t *sum_final_64 = reinterpret_cast<uint64_t*>(&sum_final_128);
+ uint64_t *sqsum_final_64 = reinterpret_cast<uint64_t*>(&sqsum_final_128);
+
+ const uint32_t pixelSum = sum_final_64[0] + sum_final_64[1];
+ const uint64_t pixelSqSum = sqsum_final_64[0] + sqsum_final_64[1];
+ const uint32_t tempDiffSum = sad_final_64[0] + sad_final_64[1];
+
+ // Default.
+ motion_magnitude_ = 0.0f;
+
+ if (tempDiffSum == 0) return VPM_OK;
+
+ // Normalize over all pixels.
+ const float tempDiffAvg = (float)tempDiffSum / (float)(num_pixels);
+ const float pixelSumAvg = (float)pixelSum / (float)(num_pixels);
+ const float pixelSqSumAvg = (float)pixelSqSum / (float)(num_pixels);
+ float contrast = pixelSqSumAvg - (pixelSumAvg * pixelSumAvg);
+
+ if (contrast > 0.0) {
+ contrast = sqrt(contrast);
+ motion_magnitude_ = tempDiffAvg/contrast;
+ }
+
+ return VPM_OK;
+}
+
+int32_t VPMContentAnalysis::ComputeSpatialMetrics_SSE2() {
+ const uint8_t* imgBuf = orig_frame_ + border_*width_;
+ const int32_t width_end = ((width_ - 2 * border_) & -16) + border_;
+
+ __m128i se_32 = _mm_setzero_si128();
+ __m128i sev_32 = _mm_setzero_si128();
+ __m128i seh_32 = _mm_setzero_si128();
+ __m128i msa_32 = _mm_setzero_si128();
+ const __m128i z = _mm_setzero_si128();
+
+ // Error is accumulated as a 32 bit value. Looking at HD content with a
+ // height of 1080 lines, or about 67 macro blocks. If the 16 bit row
+ // value is maxed out at 65529 for every row, 65529*1080 = 70777800, which
+ // will not roll over a 32 bit accumulator.
+ // skip_num_ is also used to reduce the number of rows
+ for (int32_t i = 0; i < (height_ - 2*border_); i += skip_num_) {
+ __m128i se_16 = _mm_setzero_si128();
+ __m128i sev_16 = _mm_setzero_si128();
+ __m128i seh_16 = _mm_setzero_si128();
+ __m128i msa_16 = _mm_setzero_si128();
+
+ // Row error is accumulated as a 16 bit value. There are 8
+ // accumulators. Max value of a 16 bit number is 65529. Looking
+ // at HD content, 1080p, has a width of 1920, 120 macro blocks.
+ // A mb at a time is processed at a time. Absolute max error at
+ // a point would be abs(0-255+255+255+255) which equals 1020.
+ // 120*1020 = 122400. The probability of hitting this is quite low
+ // on well behaved content. A specially crafted image could roll over.
+ // border_ could also be adjusted to concentrate on just the center of
+ // the images for an HD capture in order to reduce the possiblity of
+ // rollover.
+ const uint8_t *lineTop = imgBuf - width_ + border_;
+ const uint8_t *lineCen = imgBuf + border_;
+ const uint8_t *lineBot = imgBuf + width_ + border_;
+
+ for (int32_t j = 0; j < width_end - border_; j += 16) {
+ const __m128i t = _mm_loadu_si128((__m128i*)(lineTop));
+ const __m128i l = _mm_loadu_si128((__m128i*)(lineCen - 1));
+ const __m128i c = _mm_loadu_si128((__m128i*)(lineCen));
+ const __m128i r = _mm_loadu_si128((__m128i*)(lineCen + 1));
+ const __m128i b = _mm_loadu_si128((__m128i*)(lineBot));
+
+ lineTop += 16;
+ lineCen += 16;
+ lineBot += 16;
+
+ // center pixel unpacked
+ __m128i clo = _mm_unpacklo_epi8(c,z);
+ __m128i chi = _mm_unpackhi_epi8(c,z);
+
+ // left right pixels unpacked and added together
+ const __m128i lrlo = _mm_add_epi16(_mm_unpacklo_epi8(l,z),
+ _mm_unpacklo_epi8(r,z));
+ const __m128i lrhi = _mm_add_epi16(_mm_unpackhi_epi8(l,z),
+ _mm_unpackhi_epi8(r,z));
+
+ // top & bottom pixels unpacked and added together
+ const __m128i tblo = _mm_add_epi16(_mm_unpacklo_epi8(t,z),
+ _mm_unpacklo_epi8(b,z));
+ const __m128i tbhi = _mm_add_epi16(_mm_unpackhi_epi8(t,z),
+ _mm_unpackhi_epi8(b,z));
+
+ // running sum of all pixels
+ msa_16 = _mm_add_epi16(msa_16, _mm_add_epi16(chi, clo));
+
+ clo = _mm_slli_epi16(clo, 1);
+ chi = _mm_slli_epi16(chi, 1);
+ const __m128i sevtlo = _mm_subs_epi16(clo, tblo);
+ const __m128i sevthi = _mm_subs_epi16(chi, tbhi);
+ const __m128i sehtlo = _mm_subs_epi16(clo, lrlo);
+ const __m128i sehthi = _mm_subs_epi16(chi, lrhi);
+
+ clo = _mm_slli_epi16(clo, 1);
+ chi = _mm_slli_epi16(chi, 1);
+ const __m128i setlo = _mm_subs_epi16(clo, _mm_add_epi16(lrlo, tblo));
+ const __m128i sethi = _mm_subs_epi16(chi, _mm_add_epi16(lrhi, tbhi));
+
+ // Add to 16 bit running sum
+ se_16 = _mm_add_epi16(se_16, _mm_max_epi16(setlo,
+ _mm_subs_epi16(z, setlo)));
+ se_16 = _mm_add_epi16(se_16, _mm_max_epi16(sethi,
+ _mm_subs_epi16(z, sethi)));
+ sev_16 = _mm_add_epi16(sev_16, _mm_max_epi16(sevtlo,
+ _mm_subs_epi16(z, sevtlo)));
+ sev_16 = _mm_add_epi16(sev_16, _mm_max_epi16(sevthi,
+ _mm_subs_epi16(z, sevthi)));
+ seh_16 = _mm_add_epi16(seh_16, _mm_max_epi16(sehtlo,
+ _mm_subs_epi16(z, sehtlo)));
+ seh_16 = _mm_add_epi16(seh_16, _mm_max_epi16(sehthi,
+ _mm_subs_epi16(z, sehthi)));
+ }
+
+ // Add to 32 bit running sum as to not roll over.
+ se_32 = _mm_add_epi32(se_32, _mm_add_epi32(_mm_unpackhi_epi16(se_16,z),
+ _mm_unpacklo_epi16(se_16,z)));
+ sev_32 = _mm_add_epi32(sev_32, _mm_add_epi32(_mm_unpackhi_epi16(sev_16,z),
+ _mm_unpacklo_epi16(sev_16,z)));
+ seh_32 = _mm_add_epi32(seh_32, _mm_add_epi32(_mm_unpackhi_epi16(seh_16,z),
+ _mm_unpacklo_epi16(seh_16,z)));
+ msa_32 = _mm_add_epi32(msa_32, _mm_add_epi32(_mm_unpackhi_epi16(msa_16,z),
+ _mm_unpacklo_epi16(msa_16,z)));
+
+ imgBuf += width_ * skip_num_;
+ }
+
+ __m128i se_128;
+ __m128i sev_128;
+ __m128i seh_128;
+ __m128i msa_128;
+
+ // Bring sums out of vector registers and into integer register
+ // domain, summing them along the way.
+ _mm_store_si128 (&se_128, _mm_add_epi64(_mm_unpackhi_epi32(se_32,z),
+ _mm_unpacklo_epi32(se_32,z)));
+ _mm_store_si128 (&sev_128, _mm_add_epi64(_mm_unpackhi_epi32(sev_32,z),
+ _mm_unpacklo_epi32(sev_32,z)));
+ _mm_store_si128 (&seh_128, _mm_add_epi64(_mm_unpackhi_epi32(seh_32,z),
+ _mm_unpacklo_epi32(seh_32,z)));
+ _mm_store_si128 (&msa_128, _mm_add_epi64(_mm_unpackhi_epi32(msa_32,z),
+ _mm_unpacklo_epi32(msa_32,z)));
+
+ uint64_t *se_64 = reinterpret_cast<uint64_t*>(&se_128);
+ uint64_t *sev_64 = reinterpret_cast<uint64_t*>(&sev_128);
+ uint64_t *seh_64 = reinterpret_cast<uint64_t*>(&seh_128);
+ uint64_t *msa_64 = reinterpret_cast<uint64_t*>(&msa_128);
+
+ const uint32_t spatialErrSum = se_64[0] + se_64[1];
+ const uint32_t spatialErrVSum = sev_64[0] + sev_64[1];
+ const uint32_t spatialErrHSum = seh_64[0] + seh_64[1];
+ const uint32_t pixelMSA = msa_64[0] + msa_64[1];
+
+ // Normalize over all pixels.
+ const float spatialErr = (float)(spatialErrSum >> 2);
+ const float spatialErrH = (float)(spatialErrHSum >> 1);
+ const float spatialErrV = (float)(spatialErrVSum >> 1);
+ const float norm = (float)pixelMSA;
+
+ // 2X2:
+ spatial_pred_err_ = spatialErr / norm;
+
+ // 1X2:
+ spatial_pred_err_h_ = spatialErrH / norm;
+
+ // 2X1:
+ spatial_pred_err_v_ = spatialErrV / norm;
+
+ return VPM_OK;
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/video_processing/main/source/deflickering.cc b/webrtc/modules/video_processing/main/source/deflickering.cc
new file mode 100644
index 0000000000..19bc641ac9
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/deflickering.cc
@@ -0,0 +1,398 @@
+/*
+ * 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 "webrtc/modules/video_processing/main/source/deflickering.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"
+#include "webrtc/system_wrappers/include/logging.h"
+#include "webrtc/system_wrappers/include/sort.h"
+
+namespace webrtc {
+
+// Detection constants
+// (Q4) Maximum allowed deviation for detection.
+enum { kFrequencyDeviation = 39 };
+// (Q4) Minimum frequency that can be detected.
+enum { kMinFrequencyToDetect = 32 };
+// Number of flickers before we accept detection
+enum { kNumFlickerBeforeDetect = 2 };
+enum { kmean_valueScaling = 4 }; // (Q4) In power of 2
+// Dead-zone region in terms of pixel values
+enum { kZeroCrossingDeadzone = 10 };
+// Deflickering constants.
+// Compute the quantiles over 1 / DownsamplingFactor of the image.
+enum { kDownsamplingFactor = 8 };
+enum { kLog2OfDownsamplingFactor = 3 };
+
+// To generate in Matlab:
+// >> probUW16 = round(2^11 *
+// [0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,0.95,0.97]);
+// >> fprintf('%d, ', probUW16)
+// Resolution reduced to avoid overflow when multiplying with the
+// (potentially) large number of pixels.
+const uint16_t VPMDeflickering::prob_uw16_[kNumProbs] = {102, 205, 410, 614,
+ 819, 1024, 1229, 1434, 1638, 1843, 1946, 1987}; // <Q11>
+
+// To generate in Matlab:
+// >> numQuants = 14; maxOnlyLength = 5;
+// >> weightUW16 = round(2^15 *
+// [linspace(0.5, 1.0, numQuants - maxOnlyLength)]);
+// >> fprintf('%d, %d,\n ', weightUW16);
+const uint16_t VPMDeflickering::weight_uw16_[kNumQuants - kMaxOnlyLength] =
+ {16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768}; // <Q15>
+
+VPMDeflickering::VPMDeflickering() {
+ Reset();
+}
+
+VPMDeflickering::~VPMDeflickering() {}
+
+void VPMDeflickering::Reset() {
+ mean_buffer_length_ = 0;
+ detection_state_ = 0;
+ frame_rate_ = 0;
+
+ memset(mean_buffer_, 0, sizeof(int32_t) * kMeanBufferLength);
+ memset(timestamp_buffer_, 0, sizeof(int32_t) * kMeanBufferLength);
+
+ // Initialize the history with a uniformly distributed histogram.
+ quant_hist_uw8_[0][0] = 0;
+ quant_hist_uw8_[0][kNumQuants - 1] = 255;
+ for (int32_t i = 0; i < kNumProbs; i++) {
+ // Unsigned round. <Q0>
+ quant_hist_uw8_[0][i + 1] = static_cast<uint8_t>(
+ (prob_uw16_[i] * 255 + (1 << 10)) >> 11);
+ }
+
+ for (int32_t i = 1; i < kFrameHistory_size; i++) {
+ memcpy(quant_hist_uw8_[i], quant_hist_uw8_[0],
+ sizeof(uint8_t) * kNumQuants);
+ }
+}
+
+int32_t VPMDeflickering::ProcessFrame(
+ VideoFrame* frame,
+ VideoProcessingModule::FrameStats* stats) {
+ assert(frame);
+ uint32_t frame_memory;
+ uint8_t quant_uw8[kNumQuants];
+ uint8_t maxquant_uw8[kNumQuants];
+ uint8_t minquant_uw8[kNumQuants];
+ uint16_t target_quant_uw16[kNumQuants];
+ uint16_t increment_uw16;
+ uint8_t map_uw8[256];
+
+ uint16_t tmp_uw16;
+ uint32_t tmp_uw32;
+ int width = frame->width();
+ int height = frame->height();
+
+ if (frame->IsZeroSize()) {
+ return VPM_GENERAL_ERROR;
+ }
+
+ // Stricter height check due to subsampling size calculation below.
+ if (height < 2) {
+ LOG(LS_ERROR) << "Invalid frame size.";
+ return VPM_GENERAL_ERROR;
+ }
+
+ if (!VideoProcessingModule::ValidFrameStats(*stats)) {
+ return VPM_GENERAL_ERROR;
+ }
+
+ if (PreDetection(frame->timestamp(), *stats) == -1) return VPM_GENERAL_ERROR;
+
+ // Flicker detection
+ int32_t det_flicker = DetectFlicker();
+ if (det_flicker < 0) {
+ return VPM_GENERAL_ERROR;
+ } else if (det_flicker != 1) {
+ return 0;
+ }
+
+ // Size of luminance component.
+ const uint32_t y_size = height * width;
+
+ const uint32_t y_sub_size = width * (((height - 1) >>
+ kLog2OfDownsamplingFactor) + 1);
+ uint8_t* y_sorted = new uint8_t[y_sub_size];
+ uint32_t sort_row_idx = 0;
+ for (int i = 0; i < height; i += kDownsamplingFactor) {
+ memcpy(y_sorted + sort_row_idx * width,
+ frame->buffer(kYPlane) + i * width, width);
+ sort_row_idx++;
+ }
+
+ webrtc::Sort(y_sorted, y_sub_size, webrtc::TYPE_UWord8);
+
+ uint32_t prob_idx_uw32 = 0;
+ quant_uw8[0] = 0;
+ quant_uw8[kNumQuants - 1] = 255;
+
+ // Ensure we won't get an overflow below.
+ // In practice, the number of subsampled pixels will not become this large.
+ if (y_sub_size > (1 << 21) - 1) {
+ LOG(LS_ERROR) << "Subsampled number of pixels too large.";
+ return -1;
+ }
+
+ for (int32_t i = 0; i < kNumProbs; i++) {
+ // <Q0>.
+ prob_idx_uw32 = WEBRTC_SPL_UMUL_32_16(y_sub_size, prob_uw16_[i]) >> 11;
+ quant_uw8[i + 1] = y_sorted[prob_idx_uw32];
+ }
+
+ delete [] y_sorted;
+ y_sorted = NULL;
+
+ // Shift history for new frame.
+ memmove(quant_hist_uw8_[1], quant_hist_uw8_[0],
+ (kFrameHistory_size - 1) * kNumQuants * sizeof(uint8_t));
+ // Store current frame in history.
+ memcpy(quant_hist_uw8_[0], quant_uw8, kNumQuants * sizeof(uint8_t));
+
+ // We use a frame memory equal to the ceiling of half the frame rate to
+ // ensure we capture an entire period of flicker.
+ frame_memory = (frame_rate_ + (1 << 5)) >> 5; // Unsigned ceiling. <Q0>
+ // frame_rate_ in Q4.
+ if (frame_memory > kFrameHistory_size) {
+ frame_memory = kFrameHistory_size;
+ }
+
+ // Get maximum and minimum.
+ for (int32_t i = 0; i < kNumQuants; i++) {
+ maxquant_uw8[i] = 0;
+ minquant_uw8[i] = 255;
+ for (uint32_t j = 0; j < frame_memory; j++) {
+ if (quant_hist_uw8_[j][i] > maxquant_uw8[i]) {
+ maxquant_uw8[i] = quant_hist_uw8_[j][i];
+ }
+
+ if (quant_hist_uw8_[j][i] < minquant_uw8[i]) {
+ minquant_uw8[i] = quant_hist_uw8_[j][i];
+ }
+ }
+ }
+
+ // Get target quantiles.
+ for (int32_t i = 0; i < kNumQuants - kMaxOnlyLength; i++) {
+ // target = w * maxquant_uw8 + (1 - w) * minquant_uw8
+ // Weights w = |weight_uw16_| are in Q15, hence the final output has to be
+ // right shifted by 8 to end up in Q7.
+ target_quant_uw16[i] = static_cast<uint16_t>((
+ weight_uw16_[i] * maxquant_uw8[i] +
+ ((1 << 15) - weight_uw16_[i]) * minquant_uw8[i]) >> 8); // <Q7>
+ }
+
+ for (int32_t i = kNumQuants - kMaxOnlyLength; i < kNumQuants; i++) {
+ target_quant_uw16[i] = ((uint16_t)maxquant_uw8[i]) << 7;
+ }
+
+ // Compute the map from input to output pixels.
+ uint16_t mapUW16; // <Q7>
+ for (int32_t i = 1; i < kNumQuants; i++) {
+ // As quant and targetQuant are limited to UWord8, it's safe to use Q7 here.
+ tmp_uw32 = static_cast<uint32_t>(target_quant_uw16[i] -
+ target_quant_uw16[i - 1]);
+ tmp_uw16 = static_cast<uint16_t>(quant_uw8[i] - quant_uw8[i - 1]); // <Q0>
+
+ if (tmp_uw16 > 0) {
+ increment_uw16 = static_cast<uint16_t>(WebRtcSpl_DivU32U16(tmp_uw32,
+ tmp_uw16)); // <Q7>
+ } else {
+ // The value is irrelevant; the loop below will only iterate once.
+ increment_uw16 = 0;
+ }
+
+ mapUW16 = target_quant_uw16[i - 1];
+ for (uint32_t j = quant_uw8[i - 1]; j < (uint32_t)(quant_uw8[i] + 1); j++) {
+ // Unsigned round. <Q0>
+ map_uw8[j] = (uint8_t)((mapUW16 + (1 << 6)) >> 7);
+ mapUW16 += increment_uw16;
+ }
+ }
+
+ // Map to the output frame.
+ uint8_t* buffer = frame->buffer(kYPlane);
+ for (uint32_t i = 0; i < y_size; i++) {
+ buffer[i] = map_uw8[buffer[i]];
+ }
+
+ // Frame was altered, so reset stats.
+ VideoProcessingModule::ClearFrameStats(stats);
+
+ return VPM_OK;
+}
+
+/**
+ Performs some pre-detection operations. Must be called before
+ DetectFlicker().
+
+ \param[in] timestamp Timestamp of the current frame.
+ \param[in] stats Statistics of the current frame.
+
+ \return 0: Success\n
+ 2: Detection not possible due to flickering frequency too close to
+ zero.\n
+ -1: Error
+*/
+int32_t VPMDeflickering::PreDetection(const uint32_t timestamp,
+ const VideoProcessingModule::FrameStats& stats) {
+ int32_t mean_val; // Mean value of frame (Q4)
+ uint32_t frame_rate = 0;
+ int32_t meanBufferLength; // Temp variable.
+
+ mean_val = ((stats.sum << kmean_valueScaling) / stats.num_pixels);
+ // Update mean value buffer.
+ // This should be done even though we might end up in an unreliable detection.
+ memmove(mean_buffer_ + 1, mean_buffer_,
+ (kMeanBufferLength - 1) * sizeof(int32_t));
+ mean_buffer_[0] = mean_val;
+
+ // Update timestamp buffer.
+ // This should be done even though we might end up in an unreliable detection.
+ memmove(timestamp_buffer_ + 1, timestamp_buffer_, (kMeanBufferLength - 1) *
+ sizeof(uint32_t));
+ timestamp_buffer_[0] = timestamp;
+
+/* Compute current frame rate (Q4) */
+ if (timestamp_buffer_[kMeanBufferLength - 1] != 0) {
+ frame_rate = ((90000 << 4) * (kMeanBufferLength - 1));
+ frame_rate /=
+ (timestamp_buffer_[0] - timestamp_buffer_[kMeanBufferLength - 1]);
+ } else if (timestamp_buffer_[1] != 0) {
+ frame_rate = (90000 << 4) / (timestamp_buffer_[0] - timestamp_buffer_[1]);
+ }
+
+ /* Determine required size of mean value buffer (mean_buffer_length_) */
+ if (frame_rate == 0) {
+ meanBufferLength = 1;
+ } else {
+ meanBufferLength =
+ (kNumFlickerBeforeDetect * frame_rate) / kMinFrequencyToDetect;
+ }
+ /* Sanity check of buffer length */
+ if (meanBufferLength >= kMeanBufferLength) {
+ /* Too long buffer. The flickering frequency is too close to zero, which
+ * makes the estimation unreliable.
+ */
+ mean_buffer_length_ = 0;
+ return 2;
+ }
+ mean_buffer_length_ = meanBufferLength;
+
+ if ((timestamp_buffer_[mean_buffer_length_ - 1] != 0) &&
+ (mean_buffer_length_ != 1)) {
+ frame_rate = ((90000 << 4) * (mean_buffer_length_ - 1));
+ frame_rate /=
+ (timestamp_buffer_[0] - timestamp_buffer_[mean_buffer_length_ - 1]);
+ } else if (timestamp_buffer_[1] != 0) {
+ frame_rate = (90000 << 4) / (timestamp_buffer_[0] - timestamp_buffer_[1]);
+ }
+ frame_rate_ = frame_rate;
+
+ return VPM_OK;
+}
+
+/**
+ This function detects flicker in the video stream. As a side effect the
+ mean value buffer is updated with the new mean value.
+
+ \return 0: No flickering detected\n
+ 1: Flickering detected\n
+ 2: Detection not possible due to unreliable frequency interval
+ -1: Error
+*/
+int32_t VPMDeflickering::DetectFlicker() {
+ uint32_t i;
+ int32_t freqEst; // (Q4) Frequency estimate to base detection upon
+ int32_t ret_val = -1;
+
+ /* Sanity check for mean_buffer_length_ */
+ if (mean_buffer_length_ < 2) {
+ /* Not possible to estimate frequency */
+ return(2);
+ }
+ // Count zero crossings with a dead zone to be robust against noise. If the
+ // noise std is 2 pixel this corresponds to about 95% confidence interval.
+ int32_t deadzone = (kZeroCrossingDeadzone << kmean_valueScaling); // Q4
+ int32_t meanOfBuffer = 0; // Mean value of mean value buffer.
+ int32_t numZeros = 0; // Number of zeros that cross the dead-zone.
+ int32_t cntState = 0; // State variable for zero crossing regions.
+ int32_t cntStateOld = 0; // Previous state for zero crossing regions.
+
+ for (i = 0; i < mean_buffer_length_; i++) {
+ meanOfBuffer += mean_buffer_[i];
+ }
+ meanOfBuffer += (mean_buffer_length_ >> 1); // Rounding, not truncation.
+ meanOfBuffer /= mean_buffer_length_;
+
+ // Count zero crossings.
+ cntStateOld = (mean_buffer_[0] >= (meanOfBuffer + deadzone));
+ cntStateOld -= (mean_buffer_[0] <= (meanOfBuffer - deadzone));
+ for (i = 1; i < mean_buffer_length_; i++) {
+ cntState = (mean_buffer_[i] >= (meanOfBuffer + deadzone));
+ cntState -= (mean_buffer_[i] <= (meanOfBuffer - deadzone));
+ if (cntStateOld == 0) {
+ cntStateOld = -cntState;
+ }
+ if (((cntState + cntStateOld) == 0) && (cntState != 0)) {
+ numZeros++;
+ cntStateOld = cntState;
+ }
+ }
+ // END count zero crossings.
+
+ /* Frequency estimation according to:
+ * freqEst = numZeros * frame_rate / 2 / mean_buffer_length_;
+ *
+ * Resolution is set to Q4
+ */
+ freqEst = ((numZeros * 90000) << 3);
+ freqEst /=
+ (timestamp_buffer_[0] - timestamp_buffer_[mean_buffer_length_ - 1]);
+
+ /* Translate frequency estimate to regions close to 100 and 120 Hz */
+ uint8_t freqState = 0; // Current translation state;
+ // (0) Not in interval,
+ // (1) Within valid interval,
+ // (2) Out of range
+ int32_t freqAlias = freqEst;
+ if (freqEst > kMinFrequencyToDetect) {
+ uint8_t aliasState = 1;
+ while(freqState == 0) {
+ /* Increase frequency */
+ freqAlias += (aliasState * frame_rate_);
+ freqAlias += ((freqEst << 1) * (1 - (aliasState << 1)));
+ /* Compute state */
+ freqState = (abs(freqAlias - (100 << 4)) <= kFrequencyDeviation);
+ freqState += (abs(freqAlias - (120 << 4)) <= kFrequencyDeviation);
+ freqState += 2 * (freqAlias > ((120 << 4) + kFrequencyDeviation));
+ /* Switch alias state */
+ aliasState++;
+ aliasState &= 0x01;
+ }
+ }
+ /* Is frequency estimate within detection region? */
+ if (freqState == 1) {
+ ret_val = 1;
+ } else if (freqState == 0) {
+ ret_val = 2;
+ } else {
+ ret_val = 0;
+ }
+ return ret_val;
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/video_processing/main/source/deflickering.h b/webrtc/modules/video_processing/main/source/deflickering.h
new file mode 100644
index 0000000000..36e6845d71
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/deflickering.h
@@ -0,0 +1,56 @@
+/*
+ * 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_MODULES_VIDEO_PROCESSING_MAIN_SOURCEdeflickering__H
+#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCEdeflickering__H
+
+#include <string.h> // NULL
+
+#include "webrtc/modules/video_processing/main/interface/video_processing.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+class VPMDeflickering {
+ public:
+ VPMDeflickering();
+ ~VPMDeflickering();
+
+ void Reset();
+ int32_t ProcessFrame(VideoFrame* frame,
+ VideoProcessingModule::FrameStats* stats);
+
+ private:
+ int32_t PreDetection(uint32_t timestamp,
+ const VideoProcessingModule::FrameStats& stats);
+
+ int32_t DetectFlicker();
+
+ enum { kMeanBufferLength = 32 };
+ enum { kFrameHistory_size = 15 };
+ enum { kNumProbs = 12 };
+ enum { kNumQuants = kNumProbs + 2 };
+ enum { kMaxOnlyLength = 5 };
+
+ uint32_t mean_buffer_length_;
+ uint8_t detection_state_; // 0: No flickering
+ // 1: Flickering detected
+ // 2: In flickering
+ int32_t mean_buffer_[kMeanBufferLength];
+ uint32_t timestamp_buffer_[kMeanBufferLength];
+ uint32_t frame_rate_;
+ static const uint16_t prob_uw16_[kNumProbs];
+ static const uint16_t weight_uw16_[kNumQuants - kMaxOnlyLength];
+ uint8_t quant_hist_uw8_[kFrameHistory_size][kNumQuants];
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCEdeflickering__H
diff --git a/webrtc/modules/video_processing/main/source/frame_preprocessor.cc b/webrtc/modules/video_processing/main/source/frame_preprocessor.cc
new file mode 100644
index 0000000000..a9d77c2e0c
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/frame_preprocessor.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/video_processing/main/source/frame_preprocessor.h"
+
+namespace webrtc {
+
+VPMFramePreprocessor::VPMFramePreprocessor()
+ : content_metrics_(NULL),
+ resampled_frame_(),
+ enable_ca_(false),
+ frame_cnt_(0) {
+ spatial_resampler_ = new VPMSimpleSpatialResampler();
+ ca_ = new VPMContentAnalysis(true);
+ vd_ = new VPMVideoDecimator();
+}
+
+VPMFramePreprocessor::~VPMFramePreprocessor() {
+ Reset();
+ delete spatial_resampler_;
+ delete ca_;
+ delete vd_;
+}
+
+void VPMFramePreprocessor::Reset() {
+ ca_->Release();
+ vd_->Reset();
+ content_metrics_ = NULL;
+ spatial_resampler_->Reset();
+ enable_ca_ = false;
+ frame_cnt_ = 0;
+}
+
+void VPMFramePreprocessor::EnableTemporalDecimation(bool enable) {
+ vd_->EnableTemporalDecimation(enable);
+}
+
+void VPMFramePreprocessor::EnableContentAnalysis(bool enable) {
+ enable_ca_ = enable;
+}
+
+void VPMFramePreprocessor::SetInputFrameResampleMode(
+ VideoFrameResampling resampling_mode) {
+ spatial_resampler_->SetInputFrameResampleMode(resampling_mode);
+}
+
+int32_t VPMFramePreprocessor::SetTargetResolution(
+ uint32_t width, uint32_t height, uint32_t frame_rate) {
+ if ( (width == 0) || (height == 0) || (frame_rate == 0)) {
+ return VPM_PARAMETER_ERROR;
+ }
+ int32_t ret_val = 0;
+ ret_val = spatial_resampler_->SetTargetFrameSize(width, height);
+
+ if (ret_val < 0) return ret_val;
+
+ vd_->SetTargetFramerate(frame_rate);
+ return VPM_OK;
+}
+
+void VPMFramePreprocessor::SetTargetFramerate(int frame_rate) {
+ if (frame_rate == -1) {
+ vd_->EnableTemporalDecimation(false);
+ } else {
+ vd_->EnableTemporalDecimation(true);
+ vd_->SetTargetFramerate(frame_rate);
+ }
+}
+
+void VPMFramePreprocessor::UpdateIncomingframe_rate() {
+ vd_->UpdateIncomingframe_rate();
+}
+
+uint32_t VPMFramePreprocessor::Decimatedframe_rate() {
+ return vd_->Decimatedframe_rate();
+}
+
+
+uint32_t VPMFramePreprocessor::DecimatedWidth() const {
+ return spatial_resampler_->TargetWidth();
+}
+
+
+uint32_t VPMFramePreprocessor::DecimatedHeight() const {
+ return spatial_resampler_->TargetHeight();
+}
+
+int32_t VPMFramePreprocessor::PreprocessFrame(const VideoFrame& frame,
+ VideoFrame** processed_frame) {
+ if (frame.IsZeroSize()) {
+ return VPM_PARAMETER_ERROR;
+ }
+
+ vd_->UpdateIncomingframe_rate();
+
+ if (vd_->DropFrame()) {
+ return 1; // drop 1 frame
+ }
+
+ // Resizing incoming frame if needed. Otherwise, remains NULL.
+ // We are not allowed to resample the input frame (must make a copy of it).
+ *processed_frame = NULL;
+ if (spatial_resampler_->ApplyResample(frame.width(), frame.height())) {
+ int32_t ret = spatial_resampler_->ResampleFrame(frame, &resampled_frame_);
+ if (ret != VPM_OK) return ret;
+ *processed_frame = &resampled_frame_;
+ }
+
+ // Perform content analysis on the frame to be encoded.
+ if (enable_ca_) {
+ // Compute new metrics every |kSkipFramesCA| frames, starting with
+ // the first frame.
+ if (frame_cnt_ % kSkipFrameCA == 0) {
+ if (*processed_frame == NULL) {
+ content_metrics_ = ca_->ComputeContentMetrics(frame);
+ } else {
+ content_metrics_ = ca_->ComputeContentMetrics(resampled_frame_);
+ }
+ }
+ ++frame_cnt_;
+ }
+ return VPM_OK;
+}
+
+VideoContentMetrics* VPMFramePreprocessor::ContentMetrics() const {
+ return content_metrics_;
+}
+
+} // namespace
diff --git a/webrtc/modules/video_processing/main/source/frame_preprocessor.h b/webrtc/modules/video_processing/main/source/frame_preprocessor.h
new file mode 100644
index 0000000000..895e457cc6
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/frame_preprocessor.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+/*
+ * frame_preprocessor.h
+ */
+#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_FRAME_PREPROCESSOR_H
+#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_FRAME_PREPROCESSOR_H
+
+#include "webrtc/modules/video_processing/main/interface/video_processing.h"
+#include "webrtc/modules/video_processing/main/source/content_analysis.h"
+#include "webrtc/modules/video_processing/main/source/spatial_resampler.h"
+#include "webrtc/modules/video_processing/main/source/video_decimator.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+class VPMFramePreprocessor {
+ public:
+ VPMFramePreprocessor();
+ ~VPMFramePreprocessor();
+
+ void Reset();
+
+ // Enable temporal decimation.
+ void EnableTemporalDecimation(bool enable);
+
+ void SetInputFrameResampleMode(VideoFrameResampling resampling_mode);
+
+ // Enable content analysis.
+ void EnableContentAnalysis(bool enable);
+
+ // Set target resolution: frame rate and dimension.
+ int32_t SetTargetResolution(uint32_t width, uint32_t height,
+ uint32_t frame_rate);
+
+ // Set target frame rate.
+ void SetTargetFramerate(int frame_rate);
+
+ // Update incoming frame rate/dimension.
+ void UpdateIncomingframe_rate();
+
+ int32_t updateIncomingFrameSize(uint32_t width, uint32_t height);
+
+ // Set decimated values: frame rate/dimension.
+ uint32_t Decimatedframe_rate();
+ uint32_t DecimatedWidth() const;
+ uint32_t DecimatedHeight() const;
+
+ // Preprocess output:
+ int32_t PreprocessFrame(const VideoFrame& frame,
+ VideoFrame** processed_frame);
+ VideoContentMetrics* ContentMetrics() const;
+
+ private:
+ // The content does not change so much every frame, so to reduce complexity
+ // we can compute new content metrics every |kSkipFrameCA| frames.
+ enum { kSkipFrameCA = 2 };
+
+ VideoContentMetrics* content_metrics_;
+ VideoFrame resampled_frame_;
+ VPMSpatialResampler* spatial_resampler_;
+ VPMContentAnalysis* ca_;
+ VPMVideoDecimator* vd_;
+ bool enable_ca_;
+ int frame_cnt_;
+
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_FRAME_PREPROCESSOR_H
diff --git a/webrtc/modules/video_processing/main/source/spatial_resampler.cc b/webrtc/modules/video_processing/main/source/spatial_resampler.cc
new file mode 100644
index 0000000000..9360e68b41
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/spatial_resampler.cc
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/video_processing/main/source/spatial_resampler.h"
+
+
+namespace webrtc {
+
+VPMSimpleSpatialResampler::VPMSimpleSpatialResampler()
+ : resampling_mode_(kFastRescaling),
+ target_width_(0),
+ target_height_(0),
+ scaler_() {}
+
+VPMSimpleSpatialResampler::~VPMSimpleSpatialResampler() {}
+
+
+int32_t VPMSimpleSpatialResampler::SetTargetFrameSize(int32_t width,
+ int32_t height) {
+ if (resampling_mode_ == kNoRescaling) return VPM_OK;
+
+ if (width < 1 || height < 1) return VPM_PARAMETER_ERROR;
+
+ target_width_ = width;
+ target_height_ = height;
+
+ return VPM_OK;
+}
+
+void VPMSimpleSpatialResampler::SetInputFrameResampleMode(
+ VideoFrameResampling resampling_mode) {
+ resampling_mode_ = resampling_mode;
+}
+
+void VPMSimpleSpatialResampler::Reset() {
+ resampling_mode_ = kFastRescaling;
+ target_width_ = 0;
+ target_height_ = 0;
+}
+
+int32_t VPMSimpleSpatialResampler::ResampleFrame(const VideoFrame& inFrame,
+ VideoFrame* outFrame) {
+ // Don't copy if frame remains as is.
+ if (resampling_mode_ == kNoRescaling)
+ return VPM_OK;
+ // Check if re-sampling is needed
+ else if ((inFrame.width() == target_width_) &&
+ (inFrame.height() == target_height_)) {
+ return VPM_OK;
+ }
+
+ // Setting scaler
+ // TODO(mikhal/marpan): Should we allow for setting the filter mode in
+ // _scale.Set() with |resampling_mode_|?
+ int ret_val = 0;
+ ret_val = scaler_.Set(inFrame.width(), inFrame.height(),
+ target_width_, target_height_, kI420, kI420, kScaleBox);
+ if (ret_val < 0)
+ return ret_val;
+
+ ret_val = scaler_.Scale(inFrame, outFrame);
+
+ // Setting time parameters to the output frame.
+ // Timestamp will be reset in Scale call above, so we should set it after.
+ outFrame->set_timestamp(inFrame.timestamp());
+ outFrame->set_render_time_ms(inFrame.render_time_ms());
+
+ if (ret_val == 0)
+ return VPM_OK;
+ else
+ return VPM_SCALE_ERROR;
+}
+
+int32_t VPMSimpleSpatialResampler::TargetHeight() {
+ return target_height_;
+}
+
+int32_t VPMSimpleSpatialResampler::TargetWidth() {
+ return target_width_;
+}
+
+bool VPMSimpleSpatialResampler::ApplyResample(int32_t width,
+ int32_t height) {
+ if ((width == target_width_ && height == target_height_) ||
+ resampling_mode_ == kNoRescaling)
+ return false;
+ else
+ return true;
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/video_processing/main/source/spatial_resampler.h b/webrtc/modules/video_processing/main/source/spatial_resampler.h
new file mode 100644
index 0000000000..f965a40a83
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/spatial_resampler.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_SPATIAL_RESAMPLER_H
+#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_SPATIAL_RESAMPLER_H
+
+#include "webrtc/typedefs.h"
+
+#include "webrtc/modules/interface/module_common_types.h"
+#include "webrtc/modules/video_processing/main/interface/video_processing_defines.h"
+
+#include "webrtc/common_video/libyuv/include/scaler.h"
+#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+
+namespace webrtc {
+
+class VPMSpatialResampler {
+ public:
+ virtual ~VPMSpatialResampler() {};
+ virtual int32_t SetTargetFrameSize(int32_t width, int32_t height) = 0;
+ virtual void SetInputFrameResampleMode(VideoFrameResampling
+ resampling_mode) = 0;
+ virtual void Reset() = 0;
+ virtual int32_t ResampleFrame(const VideoFrame& inFrame,
+ VideoFrame* outFrame) = 0;
+ virtual int32_t TargetWidth() = 0;
+ virtual int32_t TargetHeight() = 0;
+ virtual bool ApplyResample(int32_t width, int32_t height) = 0;
+};
+
+class VPMSimpleSpatialResampler : public VPMSpatialResampler {
+ public:
+ VPMSimpleSpatialResampler();
+ ~VPMSimpleSpatialResampler();
+ virtual int32_t SetTargetFrameSize(int32_t width, int32_t height);
+ virtual void SetInputFrameResampleMode(VideoFrameResampling resampling_mode);
+ virtual void Reset();
+ virtual int32_t ResampleFrame(const VideoFrame& inFrame,
+ VideoFrame* outFrame);
+ virtual int32_t TargetWidth();
+ virtual int32_t TargetHeight();
+ virtual bool ApplyResample(int32_t width, int32_t height);
+
+ private:
+
+ VideoFrameResampling resampling_mode_;
+ int32_t target_width_;
+ int32_t target_height_;
+ Scaler scaler_;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_SPATIAL_RESAMPLER_H
diff --git a/webrtc/modules/video_processing/main/source/video_decimator.cc b/webrtc/modules/video_processing/main/source/video_decimator.cc
new file mode 100644
index 0000000000..34c29c1677
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/video_decimator.cc
@@ -0,0 +1,146 @@
+/*
+ * 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 "webrtc/base/checks.h"
+#include "webrtc/modules/video_processing/main/interface/video_processing.h"
+#include "webrtc/modules/video_processing/main/source/video_decimator.h"
+#include "webrtc/system_wrappers/include/tick_util.h"
+
+#define VD_MIN(a, b) ((a) < (b)) ? (a) : (b)
+
+namespace webrtc {
+
+VPMVideoDecimator::VPMVideoDecimator() {
+ Reset();
+}
+
+VPMVideoDecimator::~VPMVideoDecimator() {}
+
+void VPMVideoDecimator::Reset() {
+ overshoot_modifier_ = 0;
+ drop_count_ = 0;
+ keep_count_ = 0;
+ target_frame_rate_ = 30;
+ incoming_frame_rate_ = 0.0f;
+ memset(incoming_frame_times_, 0, sizeof(incoming_frame_times_));
+ enable_temporal_decimation_ = true;
+}
+
+void VPMVideoDecimator::EnableTemporalDecimation(bool enable) {
+ enable_temporal_decimation_ = enable;
+}
+
+void VPMVideoDecimator::SetTargetFramerate(int frame_rate) {
+ RTC_DCHECK(frame_rate);
+ target_frame_rate_ = frame_rate;
+}
+
+bool VPMVideoDecimator::DropFrame() {
+ if (!enable_temporal_decimation_) return false;
+
+ if (incoming_frame_rate_ <= 0) return false;
+
+ const uint32_t incomingframe_rate =
+ static_cast<uint32_t>(incoming_frame_rate_ + 0.5f);
+
+ if (target_frame_rate_ == 0) return true;
+
+ bool drop = false;
+ if (incomingframe_rate > target_frame_rate_) {
+ int32_t overshoot =
+ overshoot_modifier_ + (incomingframe_rate - target_frame_rate_);
+ if (overshoot < 0) {
+ overshoot = 0;
+ overshoot_modifier_ = 0;
+ }
+
+ if (overshoot && 2 * overshoot < (int32_t) incomingframe_rate) {
+ if (drop_count_) { // Just got here so drop to be sure.
+ drop_count_ = 0;
+ return true;
+ }
+ const uint32_t dropVar = incomingframe_rate / overshoot;
+
+ if (keep_count_ >= dropVar) {
+ drop = true;
+ overshoot_modifier_ = -((int32_t) incomingframe_rate % overshoot) / 3;
+ keep_count_ = 1;
+ } else {
+ keep_count_++;
+ }
+ } else {
+ keep_count_ = 0;
+ const uint32_t dropVar = overshoot / target_frame_rate_;
+ if (drop_count_ < dropVar) {
+ drop = true;
+ drop_count_++;
+ } else {
+ overshoot_modifier_ = overshoot % target_frame_rate_;
+ drop = false;
+ drop_count_ = 0;
+ }
+ }
+ }
+ return drop;
+}
+
+
+uint32_t VPMVideoDecimator::Decimatedframe_rate() {
+ProcessIncomingframe_rate(TickTime::MillisecondTimestamp());
+ if (!enable_temporal_decimation_) {
+ return static_cast<uint32_t>(incoming_frame_rate_ + 0.5f);
+ }
+ return VD_MIN(target_frame_rate_,
+ static_cast<uint32_t>(incoming_frame_rate_ + 0.5f));
+}
+
+uint32_t VPMVideoDecimator::Inputframe_rate() {
+ ProcessIncomingframe_rate(TickTime::MillisecondTimestamp());
+ return static_cast<uint32_t>(incoming_frame_rate_ + 0.5f);
+}
+
+void VPMVideoDecimator::UpdateIncomingframe_rate() {
+ int64_t now = TickTime::MillisecondTimestamp();
+ if (incoming_frame_times_[0] == 0) {
+ // First no shift.
+ } else {
+ // Shift.
+ for (int i = kFrameCountHistory_size - 2; i >= 0; i--) {
+ incoming_frame_times_[i+1] = incoming_frame_times_[i];
+ }
+ }
+ incoming_frame_times_[0] = now;
+ ProcessIncomingframe_rate(now);
+}
+
+void VPMVideoDecimator::ProcessIncomingframe_rate(int64_t now) {
+ int32_t num = 0;
+ int32_t nrOfFrames = 0;
+ for (num = 1; num < (kFrameCountHistory_size - 1); num++) {
+ // Don't use data older than 2sec.
+ if (incoming_frame_times_[num] <= 0 ||
+ now - incoming_frame_times_[num] > kFrameHistoryWindowMs) {
+ break;
+ } else {
+ nrOfFrames++;
+ }
+ }
+ if (num > 1) {
+ int64_t diff = now - incoming_frame_times_[num-1];
+ incoming_frame_rate_ = 1.0;
+ if (diff > 0) {
+ incoming_frame_rate_ = nrOfFrames * 1000.0f / static_cast<float>(diff);
+ }
+ } else {
+ incoming_frame_rate_ = static_cast<float>(nrOfFrames);
+ }
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/video_processing/main/source/video_decimator.h b/webrtc/modules/video_processing/main/source/video_decimator.h
new file mode 100644
index 0000000000..3d4573caf8
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/video_decimator.h
@@ -0,0 +1,58 @@
+/*
+ * 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_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_VIDEO_DECIMATOR_H
+#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_VIDEO_DECIMATOR_H
+
+#include "webrtc/modules/interface/module_common_types.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+class VPMVideoDecimator {
+ public:
+ VPMVideoDecimator();
+ ~VPMVideoDecimator();
+
+ void Reset();
+
+ void EnableTemporalDecimation(bool enable);
+
+ void SetTargetFramerate(int frame_rate);
+
+ bool DropFrame();
+
+ void UpdateIncomingframe_rate();
+
+ // Get Decimated Frame Rate/Dimensions.
+ uint32_t Decimatedframe_rate();
+
+ // Get input frame rate.
+ uint32_t Inputframe_rate();
+
+ private:
+ void ProcessIncomingframe_rate(int64_t now);
+
+ enum { kFrameCountHistory_size = 90};
+ enum { kFrameHistoryWindowMs = 2000};
+
+ // Temporal decimation.
+ int32_t overshoot_modifier_;
+ uint32_t drop_count_;
+ uint32_t keep_count_;
+ uint32_t target_frame_rate_;
+ float incoming_frame_rate_;
+ int64_t incoming_frame_times_[kFrameCountHistory_size];
+ bool enable_temporal_decimation_;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_VIDEO_DECIMATOR_H
diff --git a/webrtc/modules/video_processing/main/source/video_processing_impl.cc b/webrtc/modules/video_processing/main/source/video_processing_impl.cc
new file mode 100644
index 0000000000..eaaf14f6ad
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/video_processing_impl.cc
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+
+#include "webrtc/modules/video_processing/main/source/video_processing_impl.h"
+#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
+#include "webrtc/system_wrappers/include/logging.h"
+
+#include <assert.h>
+
+namespace webrtc {
+
+namespace {
+void SetSubSampling(VideoProcessingModule::FrameStats* stats,
+ const int32_t width,
+ const int32_t height) {
+ if (width * height >= 640 * 480) {
+ stats->subSamplWidth = 3;
+ stats->subSamplHeight = 3;
+ } else if (width * height >= 352 * 288) {
+ stats->subSamplWidth = 2;
+ stats->subSamplHeight = 2;
+ } else if (width * height >= 176 * 144) {
+ stats->subSamplWidth = 1;
+ stats->subSamplHeight = 1;
+ } else {
+ stats->subSamplWidth = 0;
+ stats->subSamplHeight = 0;
+ }
+}
+} // namespace
+
+VideoProcessingModule* VideoProcessingModule::Create() {
+ return new VideoProcessingModuleImpl();
+}
+
+void VideoProcessingModule::Destroy(VideoProcessingModule* module) {
+ if (module)
+ delete static_cast<VideoProcessingModuleImpl*>(module);
+}
+
+VideoProcessingModuleImpl::VideoProcessingModuleImpl() {}
+VideoProcessingModuleImpl::~VideoProcessingModuleImpl() {}
+
+void VideoProcessingModuleImpl::Reset() {
+ rtc::CritScope mutex(&mutex_);
+ deflickering_.Reset();
+ brightness_detection_.Reset();
+ frame_pre_processor_.Reset();
+}
+
+int32_t VideoProcessingModule::GetFrameStats(FrameStats* stats,
+ const VideoFrame& frame) {
+ if (frame.IsZeroSize()) {
+ LOG(LS_ERROR) << "Zero size frame.";
+ return VPM_PARAMETER_ERROR;
+ }
+
+ int width = frame.width();
+ int height = frame.height();
+
+ ClearFrameStats(stats); // The histogram needs to be zeroed out.
+ SetSubSampling(stats, width, height);
+
+ const uint8_t* buffer = frame.buffer(kYPlane);
+ // Compute histogram and sum of frame
+ for (int i = 0; i < height; i += (1 << stats->subSamplHeight)) {
+ int k = i * width;
+ for (int j = 0; j < width; j += (1 << stats->subSamplWidth)) {
+ stats->hist[buffer[k + j]]++;
+ stats->sum += buffer[k + j];
+ }
+ }
+
+ stats->num_pixels = (width * height) / ((1 << stats->subSamplWidth) *
+ (1 << stats->subSamplHeight));
+ assert(stats->num_pixels > 0);
+
+ // Compute mean value of frame
+ stats->mean = stats->sum / stats->num_pixels;
+
+ return VPM_OK;
+}
+
+bool VideoProcessingModule::ValidFrameStats(const FrameStats& stats) {
+ if (stats.num_pixels == 0) {
+ LOG(LS_WARNING) << "Invalid frame stats.";
+ return false;
+ }
+ return true;
+}
+
+void VideoProcessingModule::ClearFrameStats(FrameStats* stats) {
+ stats->mean = 0;
+ stats->sum = 0;
+ stats->num_pixels = 0;
+ stats->subSamplWidth = 0;
+ stats->subSamplHeight = 0;
+ memset(stats->hist, 0, sizeof(stats->hist));
+}
+
+int32_t VideoProcessingModule::Brighten(VideoFrame* frame, int delta) {
+ return VideoProcessing::Brighten(frame, delta);
+}
+
+int32_t VideoProcessingModuleImpl::Deflickering(VideoFrame* frame,
+ FrameStats* stats) {
+ rtc::CritScope mutex(&mutex_);
+ return deflickering_.ProcessFrame(frame, stats);
+}
+
+int32_t VideoProcessingModuleImpl::BrightnessDetection(
+ const VideoFrame& frame,
+ const FrameStats& stats) {
+ rtc::CritScope mutex(&mutex_);
+ return brightness_detection_.ProcessFrame(frame, stats);
+}
+
+
+void VideoProcessingModuleImpl::EnableTemporalDecimation(bool enable) {
+ rtc::CritScope mutex(&mutex_);
+ frame_pre_processor_.EnableTemporalDecimation(enable);
+}
+
+
+void VideoProcessingModuleImpl::SetInputFrameResampleMode(VideoFrameResampling
+ resampling_mode) {
+ rtc::CritScope cs(&mutex_);
+ frame_pre_processor_.SetInputFrameResampleMode(resampling_mode);
+}
+
+int32_t VideoProcessingModuleImpl::SetTargetResolution(uint32_t width,
+ uint32_t height,
+ uint32_t frame_rate) {
+ rtc::CritScope cs(&mutex_);
+ return frame_pre_processor_.SetTargetResolution(width, height, frame_rate);
+}
+
+void VideoProcessingModuleImpl::SetTargetFramerate(int frame_rate) {
+ rtc::CritScope cs(&mutex_);
+ frame_pre_processor_.SetTargetFramerate(frame_rate);
+}
+
+uint32_t VideoProcessingModuleImpl::Decimatedframe_rate() {
+ rtc::CritScope cs(&mutex_);
+ return frame_pre_processor_.Decimatedframe_rate();
+}
+
+uint32_t VideoProcessingModuleImpl::DecimatedWidth() const {
+ rtc::CritScope cs(&mutex_);
+ return frame_pre_processor_.DecimatedWidth();
+}
+
+uint32_t VideoProcessingModuleImpl::DecimatedHeight() const {
+ rtc::CritScope cs(&mutex_);
+ return frame_pre_processor_.DecimatedHeight();
+}
+
+int32_t VideoProcessingModuleImpl::PreprocessFrame(
+ const VideoFrame& frame,
+ VideoFrame** processed_frame) {
+ rtc::CritScope mutex(&mutex_);
+ return frame_pre_processor_.PreprocessFrame(frame, processed_frame);
+}
+
+VideoContentMetrics* VideoProcessingModuleImpl::ContentMetrics() const {
+ rtc::CritScope mutex(&mutex_);
+ return frame_pre_processor_.ContentMetrics();
+}
+
+void VideoProcessingModuleImpl::EnableContentAnalysis(bool enable) {
+ rtc::CritScope mutex(&mutex_);
+ frame_pre_processor_.EnableContentAnalysis(enable);
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/video_processing/main/source/video_processing_impl.h b/webrtc/modules/video_processing/main/source/video_processing_impl.h
new file mode 100644
index 0000000000..fed5197f49
--- /dev/null
+++ b/webrtc/modules/video_processing/main/source/video_processing_impl.h
@@ -0,0 +1,75 @@
+/*
+ * 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_MODULE_VIDEO_PROCESSING_IMPL_H
+#define WEBRTC_MODULE_VIDEO_PROCESSING_IMPL_H
+
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/modules/video_processing/main/interface/video_processing.h"
+#include "webrtc/modules/video_processing/main/source/brighten.h"
+#include "webrtc/modules/video_processing/main/source/brightness_detection.h"
+#include "webrtc/modules/video_processing/main/source/deflickering.h"
+#include "webrtc/modules/video_processing/main/source/frame_preprocessor.h"
+
+namespace webrtc {
+class CriticalSectionWrapper;
+
+class VideoProcessingModuleImpl : public VideoProcessingModule {
+ public:
+ VideoProcessingModuleImpl();
+ ~VideoProcessingModuleImpl() override;
+
+ void Reset() override;
+
+ int32_t Deflickering(VideoFrame* frame, FrameStats* stats) override;
+
+ int32_t BrightnessDetection(const VideoFrame& frame,
+ const FrameStats& stats) override;
+
+ // Frame pre-processor functions
+
+ // Enable temporal decimation
+ void EnableTemporalDecimation(bool enable) override;
+
+ void SetInputFrameResampleMode(VideoFrameResampling resampling_mode) override;
+
+ // Enable content analysis
+ void EnableContentAnalysis(bool enable) override;
+
+ // Set Target Resolution: frame rate and dimension
+ int32_t SetTargetResolution(uint32_t width,
+ uint32_t height,
+ uint32_t frame_rate) override;
+
+ void SetTargetFramerate(int frame_rate) override;
+
+ // Get decimated values: frame rate/dimension
+ uint32_t Decimatedframe_rate() override;
+ uint32_t DecimatedWidth() const override;
+ uint32_t DecimatedHeight() const override;
+
+ // Preprocess:
+ // Pre-process incoming frame: Sample when needed and compute content
+ // metrics when enabled.
+ // If no resampling takes place - processed_frame is set to NULL.
+ int32_t PreprocessFrame(const VideoFrame& frame,
+ VideoFrame** processed_frame) override;
+ VideoContentMetrics* ContentMetrics() const override;
+
+ private:
+ mutable rtc::CriticalSection mutex_;
+ VPMDeflickering deflickering_ GUARDED_BY(mutex_);
+ VPMBrightnessDetection brightness_detection_;
+ VPMFramePreprocessor frame_pre_processor_;
+};
+
+} // namespace
+
+#endif
diff --git a/webrtc/modules/video_processing/main/test/unit_test/brightness_detection_test.cc b/webrtc/modules/video_processing/main/test/unit_test/brightness_detection_test.cc
new file mode 100644
index 0000000000..4d0de3ac98
--- /dev/null
+++ b/webrtc/modules/video_processing/main/test/unit_test/brightness_detection_test.cc
@@ -0,0 +1,121 @@
+/*
+ * 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 "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+#include "webrtc/modules/video_processing/main/interface/video_processing.h"
+#include "webrtc/modules/video_processing/main/test/unit_test/video_processing_unittest.h"
+#include "webrtc/test/testsupport/gtest_disable.h"
+
+using namespace webrtc;
+
+TEST_F(VideoProcessingModuleTest, DISABLED_ON_IOS(BrightnessDetection))
+{
+ uint32_t frameNum = 0;
+ int32_t brightnessWarning = 0;
+ uint32_t warningCount = 0;
+ rtc::scoped_ptr<uint8_t[]> video_buffer(new uint8_t[frame_length_]);
+ while (fread(video_buffer.get(), 1, frame_length_, source_file_) ==
+ frame_length_)
+ {
+ EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0, width_,
+ height_, 0, kVideoRotation_0, &video_frame_));
+ frameNum++;
+ VideoProcessingModule::FrameStats stats;
+ ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
+ ASSERT_GE(brightnessWarning = vpm_->BrightnessDetection(video_frame_,
+ stats), 0);
+ if (brightnessWarning != VideoProcessingModule::kNoWarning)
+ {
+ warningCount++;
+ }
+ }
+ ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
+
+ // Expect few warnings
+ float warningProportion = static_cast<float>(warningCount) / frameNum * 100;
+ printf("\nWarning proportions:\n");
+ printf("Stock foreman: %.1f %%\n", warningProportion);
+ EXPECT_LT(warningProportion, 10);
+
+ rewind(source_file_);
+ frameNum = 0;
+ warningCount = 0;
+ while (fread(video_buffer.get(), 1, frame_length_, source_file_) ==
+ frame_length_ &&
+ frameNum < 300)
+ {
+ EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0, width_,
+ height_, 0, kVideoRotation_0, &video_frame_));
+ frameNum++;
+
+ uint8_t* frame = video_frame_.buffer(kYPlane);
+ uint32_t yTmp = 0;
+ for (int yIdx = 0; yIdx < width_ * height_; yIdx++)
+ {
+ yTmp = frame[yIdx] << 1;
+ if (yTmp > 255)
+ {
+ yTmp = 255;
+ }
+ frame[yIdx] = static_cast<uint8_t>(yTmp);
+ }
+
+ VideoProcessingModule::FrameStats stats;
+ ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
+ ASSERT_GE(brightnessWarning = vpm_->BrightnessDetection(video_frame_,
+ stats), 0);
+ EXPECT_NE(VideoProcessingModule::kDarkWarning, brightnessWarning);
+ if (brightnessWarning == VideoProcessingModule::kBrightWarning)
+ {
+ warningCount++;
+ }
+ }
+ ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
+
+ // Expect many brightness warnings
+ warningProportion = static_cast<float>(warningCount) / frameNum * 100;
+ printf("Bright foreman: %.1f %%\n", warningProportion);
+ EXPECT_GT(warningProportion, 95);
+
+ rewind(source_file_);
+ frameNum = 0;
+ warningCount = 0;
+ while (fread(video_buffer.get(), 1, frame_length_, source_file_) ==
+ frame_length_ && frameNum < 300)
+ {
+ EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0, width_,
+ height_, 0, kVideoRotation_0, &video_frame_));
+ frameNum++;
+
+ uint8_t* y_plane = video_frame_.buffer(kYPlane);
+ int32_t yTmp = 0;
+ for (int yIdx = 0; yIdx < width_ * height_; yIdx++)
+ {
+ yTmp = y_plane[yIdx] >> 1;
+ y_plane[yIdx] = static_cast<uint8_t>(yTmp);
+ }
+
+ VideoProcessingModule::FrameStats stats;
+ ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
+ ASSERT_GE(brightnessWarning = vpm_->BrightnessDetection(video_frame_,
+ stats), 0);
+ EXPECT_NE(VideoProcessingModule::kBrightWarning, brightnessWarning);
+ if (brightnessWarning == VideoProcessingModule::kDarkWarning)
+ {
+ warningCount++;
+ }
+ }
+ ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
+
+ // Expect many darkness warnings
+ warningProportion = static_cast<float>(warningCount) / frameNum * 100;
+ printf("Dark foreman: %.1f %%\n\n", warningProportion);
+ EXPECT_GT(warningProportion, 90);
+}
diff --git a/webrtc/modules/video_processing/main/test/unit_test/content_metrics_test.cc b/webrtc/modules/video_processing/main/test/unit_test/content_metrics_test.cc
new file mode 100644
index 0000000000..d9c1309d9b
--- /dev/null
+++ b/webrtc/modules/video_processing/main/test/unit_test/content_metrics_test.cc
@@ -0,0 +1,44 @@
+/*
+ * 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 "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+#include "webrtc/modules/video_processing/main/interface/video_processing.h"
+#include "webrtc/modules/video_processing/main/source/content_analysis.h"
+#include "webrtc/modules/video_processing/main/test/unit_test/video_processing_unittest.h"
+#include "webrtc/test/testsupport/gtest_disable.h"
+
+namespace webrtc {
+
+TEST_F(VideoProcessingModuleTest, DISABLED_ON_IOS(ContentAnalysis)) {
+ VPMContentAnalysis ca__c(false);
+ VPMContentAnalysis ca__sse(true);
+ VideoContentMetrics *_cM_c, *_cM_SSE;
+
+ ca__c.Initialize(width_,height_);
+ ca__sse.Initialize(width_,height_);
+
+ rtc::scoped_ptr<uint8_t[]> video_buffer(new uint8_t[frame_length_]);
+ while (fread(video_buffer.get(), 1, frame_length_, source_file_)
+ == frame_length_) {
+ // Using ConvertToI420 to add stride to the image.
+ EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0, width_, height_,
+ 0, kVideoRotation_0, &video_frame_));
+ _cM_c = ca__c.ComputeContentMetrics(video_frame_);
+ _cM_SSE = ca__sse.ComputeContentMetrics(video_frame_);
+
+ ASSERT_EQ(_cM_c->spatial_pred_err, _cM_SSE->spatial_pred_err);
+ ASSERT_EQ(_cM_c->spatial_pred_err_v, _cM_SSE->spatial_pred_err_v);
+ ASSERT_EQ(_cM_c->spatial_pred_err_h, _cM_SSE->spatial_pred_err_h);
+ ASSERT_EQ(_cM_c->motion_magnitude, _cM_SSE->motion_magnitude);
+ }
+ ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/video_processing/main/test/unit_test/createTable.m b/webrtc/modules/video_processing/main/test/unit_test/createTable.m
new file mode 100644
index 0000000000..2c7fb522f6
--- /dev/null
+++ b/webrtc/modules/video_processing/main/test/unit_test/createTable.m
@@ -0,0 +1,179 @@
+% Create the color enhancement look-up table and write it to
+% file colorEnhancementTable.cpp. Copy contents of that file into
+% the source file for the color enhancement function.
+
+clear
+close all
+
+
+% First, define the color enhancement in a normalized domain
+
+% Compander function is defined in three radial zones.
+% 1. From 0 to radius r0, the compander function
+% is a second-order polynomial intersecting the points (0,0)
+% and (r0, r0), and with a slope B in (0,0).
+% 2. From r0 to r1, the compander is a third-order polynomial
+% intersecting the points (r0, r0) and (r1, r1), and with the
+% same slope as the first part in the point (r0, r0) and slope
+% equal to 1 in (r1, r1).
+% 3. For radii larger than r1, the compander function is the
+% unity scale function (no scaling at all).
+
+r0=0.07; % Dead zone radius (must be > 0)
+r1=0.6; % Enhancement zone radius (must be > r0 and < 1)
+B=0.2; % initial slope of compander function (between 0 and 1)
+
+x0=linspace(0,r0).'; % zone 1
+x1=linspace(r0,r1).'; % zone 2
+x2=linspace(r1,1).'; % zone 3
+
+A=(1-B)/r0;
+f0=A*x0.^2+B*x0; % compander function in zone 1
+
+% equation system for finding second zone parameters
+M=[r0^3 r0^2 r0 1;
+ 3*r0^2 2*r0 1 0;
+ 3*r1^2 2*r1 1 0;
+ r1^3 r1^2 r1 1];
+m=[A*r0^2+B*r0; 2*A*r0+B; 1; r1];
+% solve equations
+theta=M\m;
+
+% compander function in zone 1
+f1=[x1.^3 x1.^2 x1 ones(size(x1))]*theta;
+
+x=[x0; x1; x2];
+f=[f0; f1; x2];
+
+% plot it
+figure(1)
+plot(x,f,x,x,':')
+xlabel('Normalized radius')
+ylabel('Modified radius')
+
+
+% Now, create the look-up table in the integer color space
+[U,V]=meshgrid(0:255, 0:255); % U-V space
+U0=U;
+V0=V;
+
+% Conversion matrix from normalized YUV to RGB
+T=[1 0 1.13983; 1 -0.39465 -0.58060; 1 2.03211 0];
+Ylum=0.5;
+
+figure(2)
+Z(:,:,1)=Ylum + (U-127)/256*T(1,2) + (V-127)/256*T(1,3);
+Z(:,:,2)=Ylum + (U-127)/256*T(2,2) + (V-127)/256*T(2,3);
+Z(:,:,3)=Ylum + (U-127)/256*T(3,2) + (V-127)/256*T(3,3);
+Z=max(Z,0);
+Z=min(Z,1);
+subplot(121)
+image(Z);
+axis square
+axis off
+set(gcf,'color','k')
+
+R = sqrt((U-127).^2 + (V-127).^2);
+Rnorm = R/127;
+RnormMod = Rnorm;
+RnormMod(RnormMod==0)=1; % avoid division with zero
+
+% find indices to pixels in dead-zone (zone 1)
+ix=find(Rnorm<=r0);
+scaleMatrix = (A*Rnorm(ix).^2 + B*Rnorm(ix))./RnormMod(ix);
+U(ix)=(U(ix)-127).*scaleMatrix+127;
+V(ix)=(V(ix)-127).*scaleMatrix+127;
+
+% find indices to pixels in zone 2
+ix=find(Rnorm>r0 & Rnorm<=r1);
+scaleMatrix = (theta(1)*Rnorm(ix).^3 + theta(2)*Rnorm(ix).^2 + ...
+ theta(3)*Rnorm(ix) + theta(4)) ./ RnormMod(ix);
+U(ix)=(U(ix)-127).*scaleMatrix + 127;
+V(ix)=(V(ix)-127).*scaleMatrix + 127;
+
+% round to integer values and saturate
+U=round(U);
+V=round(V);
+U=max(min(U,255),0);
+V=max(min(V,255),0);
+
+Z(:,:,1)=Ylum + (U-127)/256*T(1,2) + (V-127)/256*T(1,3);
+Z(:,:,2)=Ylum + (U-127)/256*T(2,2) + (V-127)/256*T(2,3);
+Z(:,:,3)=Ylum + (U-127)/256*T(3,2) + (V-127)/256*T(3,3);
+Z=max(Z,0);
+Z=min(Z,1);
+subplot(122)
+image(Z);
+axis square
+axis off
+
+figure(3)
+subplot(121)
+mesh(U-U0)
+subplot(122)
+mesh(V-V0)
+
+
+
+% Last, write to file
+% Write only one matrix, since U=V'
+
+fid = fopen('../out/Debug/colorEnhancementTable.h','wt');
+if fid==-1
+ error('Cannot open file colorEnhancementTable.cpp');
+end
+
+fprintf(fid,'//colorEnhancementTable.h\n\n');
+fprintf(fid,'//Copy the constant table to the appropriate header file.\n\n');
+
+fprintf(fid,'//Table created with Matlab script createTable.m\n\n');
+fprintf(fid,'//Usage:\n');
+fprintf(fid,'// Umod=colorTable[U][V]\n');
+fprintf(fid,'// Vmod=colorTable[V][U]\n');
+
+fprintf(fid,'static unsigned char colorTable[%i][%i] = {\n', size(U,1), size(U,2));
+
+for u=1:size(U,2)
+ fprintf(fid,' {%i', U(1,u));
+ for v=2:size(U,1)
+ fprintf(fid,', %i', U(v,u));
+ end
+ fprintf(fid,'}');
+ if u<size(U,2)
+ fprintf(fid,',');
+ end
+ fprintf(fid,'\n');
+end
+fprintf(fid,'};\n\n');
+fclose(fid);
+fprintf('done');
+
+
+answ=input('Create test vector (takes some time...)? y/n : ','s');
+if answ ~= 'y'
+ return
+end
+
+% Also, create test vectors
+
+% Read test file foreman.yuv
+fprintf('Reading test file...')
+[y,u,v]=readYUV420file('../out/Debug/testFiles/foreman_cif.yuv',352,288);
+fprintf(' done\n');
+unew=uint8(zeros(size(u)));
+vnew=uint8(zeros(size(v)));
+
+% traverse all frames
+for k=1:size(y,3)
+ fprintf('Frame %i\n', k);
+ for r=1:size(u,1)
+ for c=1:size(u,2)
+ unew(r,c,k) = uint8(U(double(v(r,c,k))+1, double(u(r,c,k))+1));
+ vnew(r,c,k) = uint8(V(double(v(r,c,k))+1, double(u(r,c,k))+1));
+ end
+ end
+end
+
+fprintf('\nWriting modified test file...')
+writeYUV420file('../out/Debug/foremanColorEnhanced.yuv',y,unew,vnew);
+fprintf(' done\n');
diff --git a/webrtc/modules/video_processing/main/test/unit_test/deflickering_test.cc b/webrtc/modules/video_processing/main/test/unit_test/deflickering_test.cc
new file mode 100644
index 0000000000..83d09ef486
--- /dev/null
+++ b/webrtc/modules/video_processing/main/test/unit_test/deflickering_test.cc
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+#include "webrtc/modules/video_processing/main/interface/video_processing.h"
+#include "webrtc/modules/video_processing/main/test/unit_test/video_processing_unittest.h"
+#include "webrtc/system_wrappers/include/tick_util.h"
+#include "webrtc/test/testsupport/fileutils.h"
+#include "webrtc/test/testsupport/gtest_disable.h"
+
+namespace webrtc {
+
+TEST_F(VideoProcessingModuleTest, DISABLED_ON_IOS(Deflickering))
+{
+ enum { NumRuns = 30 };
+ uint32_t frameNum = 0;
+ const uint32_t frame_rate = 15;
+
+ int64_t min_runtime = 0;
+ int64_t avg_runtime = 0;
+
+ // Close automatically opened Foreman.
+ fclose(source_file_);
+ const std::string input_file =
+ webrtc::test::ResourcePath("deflicker_before_cif_short", "yuv");
+ source_file_ = fopen(input_file.c_str(), "rb");
+ ASSERT_TRUE(source_file_ != NULL) <<
+ "Cannot read input file: " << input_file << "\n";
+
+ const std::string output_file =
+ webrtc::test::OutputPath() + "deflicker_output_cif_short.yuv";
+ FILE* deflickerFile = fopen(output_file.c_str(), "wb");
+ ASSERT_TRUE(deflickerFile != NULL) <<
+ "Could not open output file: " << output_file << "\n";
+
+ printf("\nRun time [us / frame]:\n");
+ rtc::scoped_ptr<uint8_t[]> video_buffer(new uint8_t[frame_length_]);
+ for (uint32_t run_idx = 0; run_idx < NumRuns; run_idx++)
+ {
+ TickTime t0;
+ TickTime t1;
+ TickInterval acc_ticks;
+ uint32_t timeStamp = 1;
+
+ frameNum = 0;
+ while (fread(video_buffer.get(), 1, frame_length_, source_file_) ==
+ frame_length_)
+ {
+ frameNum++;
+ EXPECT_EQ(
+ 0, ConvertToI420(kI420, video_buffer.get(), 0, 0, width_,
+ height_, 0, kVideoRotation_0, &video_frame_));
+ video_frame_.set_timestamp(timeStamp);
+
+ t0 = TickTime::Now();
+ VideoProcessingModule::FrameStats stats;
+ ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
+ ASSERT_EQ(0, vpm_->Deflickering(&video_frame_, &stats));
+ t1 = TickTime::Now();
+ acc_ticks += (t1 - t0);
+
+ if (run_idx == 0)
+ {
+ if (PrintVideoFrame(video_frame_, deflickerFile) < 0) {
+ return;
+ }
+ }
+ timeStamp += (90000 / frame_rate);
+ }
+ ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
+
+ printf("%u\n", static_cast<int>(acc_ticks.Microseconds() / frameNum));
+ if (acc_ticks.Microseconds() < min_runtime || run_idx == 0)
+ {
+ min_runtime = acc_ticks.Microseconds();
+ }
+ avg_runtime += acc_ticks.Microseconds();
+
+ rewind(source_file_);
+ }
+ ASSERT_EQ(0, fclose(deflickerFile));
+ // TODO(kjellander): Add verification of deflicker output file.
+
+ printf("\nAverage run time = %d us / frame\n",
+ static_cast<int>(avg_runtime / frameNum / NumRuns));
+ printf("Min run time = %d us / frame\n\n",
+ static_cast<int>(min_runtime / frameNum));
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/video_processing/main/test/unit_test/readYUV420file.m b/webrtc/modules/video_processing/main/test/unit_test/readYUV420file.m
new file mode 100644
index 0000000000..03013efd3a
--- /dev/null
+++ b/webrtc/modules/video_processing/main/test/unit_test/readYUV420file.m
@@ -0,0 +1,45 @@
+function [Y,U,V] = readYUV420file(filename, width, height)
+% [Y,U,V] = readYUVfile(filename, width, height)
+
+fid = fopen(filename,'rb');
+if fid==-1
+ error(['Cannot open file ' filename]);
+end
+
+% Number of pixels per image
+nPx=width*height;
+
+% nPx bytes luminance, nPx/4 bytes U, nPx/4 bytes V
+frameSizeBytes = nPx*1.5;
+
+% calculate number of frames
+fseek(fid,0,'eof'); % move to end of file
+fileLen=ftell(fid); % number of bytes
+fseek(fid,0,'bof'); % rewind to start
+
+% calculate number of frames
+numFrames = floor(fileLen/frameSizeBytes);
+
+Y=uint8(zeros(height,width,numFrames));
+U=uint8(zeros(height/2,width/2,numFrames));
+V=uint8(zeros(height/2,width/2,numFrames));
+
+[X,nBytes]=fread(fid, frameSizeBytes, 'uchar');
+
+for k=1:numFrames
+
+ % Store luminance
+ Y(:,:,k)=uint8(reshape(X(1:nPx), width, height).');
+
+ % Store U channel
+ U(:,:,k)=uint8(reshape(X(nPx + (1:nPx/4)), width/2, height/2).');
+
+ % Store V channel
+ V(:,:,k)=uint8(reshape(X(nPx + nPx/4 + (1:nPx/4)), width/2, height/2).');
+
+ % Read next frame
+ [X,nBytes]=fread(fid, frameSizeBytes, 'uchar');
+end
+
+
+fclose(fid);
diff --git a/webrtc/modules/video_processing/main/test/unit_test/video_processing_unittest.cc b/webrtc/modules/video_processing/main/test/unit_test/video_processing_unittest.cc
new file mode 100644
index 0000000000..11ccc4891b
--- /dev/null
+++ b/webrtc/modules/video_processing/main/test/unit_test/video_processing_unittest.cc
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/video_processing/main/test/unit_test/video_processing_unittest.h"
+
+#include <string>
+
+#include <gflags/gflags.h>
+#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+#include "webrtc/system_wrappers/include/tick_util.h"
+#include "webrtc/test/testsupport/fileutils.h"
+#include "webrtc/test/testsupport/gtest_disable.h"
+
+namespace webrtc {
+
+namespace {
+
+// Define command line flag 'gen_files' (default value: false).
+DEFINE_bool(gen_files, false, "Output files for visual inspection.");
+
+} // namespace
+
+static void PreprocessFrameAndVerify(const VideoFrame& source,
+ int target_width,
+ int target_height,
+ VideoProcessingModule* vpm,
+ VideoFrame** out_frame);
+static void CropFrame(const uint8_t* source_data,
+ int source_width,
+ int source_height,
+ int offset_x,
+ int offset_y,
+ int cropped_width,
+ int cropped_height,
+ VideoFrame* cropped_frame);
+// The |source_data| is cropped and scaled to |target_width| x |target_height|,
+// and then scaled back to the expected cropped size. |expected_psnr| is used to
+// verify basic quality, and is set to be ~0.1/0.05dB lower than actual PSNR
+// verified under the same conditions.
+static void TestSize(const VideoFrame& source_frame,
+ const VideoFrame& cropped_source_frame,
+ int target_width,
+ int target_height,
+ double expected_psnr,
+ VideoProcessingModule* vpm);
+static bool CompareFrames(const webrtc::VideoFrame& frame1,
+ const webrtc::VideoFrame& frame2);
+static void WriteProcessedFrameForVisualInspection(const VideoFrame& source,
+ const VideoFrame& processed);
+
+VideoProcessingModuleTest::VideoProcessingModuleTest()
+ : vpm_(NULL),
+ source_file_(NULL),
+ width_(352),
+ half_width_((width_ + 1) / 2),
+ height_(288),
+ size_y_(width_ * height_),
+ size_uv_(half_width_ * ((height_ + 1) / 2)),
+ frame_length_(CalcBufferSize(kI420, width_, height_)) {}
+
+void VideoProcessingModuleTest::SetUp() {
+ vpm_ = VideoProcessingModule::Create();
+ ASSERT_TRUE(vpm_ != NULL);
+
+ ASSERT_EQ(0, video_frame_.CreateEmptyFrame(width_, height_, width_,
+ half_width_, half_width_));
+ // Clear video frame so DrMemory/Valgrind will allow reads of the buffer.
+ memset(video_frame_.buffer(kYPlane), 0, video_frame_.allocated_size(kYPlane));
+ memset(video_frame_.buffer(kUPlane), 0, video_frame_.allocated_size(kUPlane));
+ memset(video_frame_.buffer(kVPlane), 0, video_frame_.allocated_size(kVPlane));
+ const std::string video_file =
+ webrtc::test::ResourcePath("foreman_cif", "yuv");
+ source_file_ = fopen(video_file.c_str(),"rb");
+ ASSERT_TRUE(source_file_ != NULL) <<
+ "Cannot read source file: " + video_file + "\n";
+}
+
+void VideoProcessingModuleTest::TearDown() {
+ if (source_file_ != NULL) {
+ ASSERT_EQ(0, fclose(source_file_));
+ }
+ source_file_ = NULL;
+
+ if (vpm_ != NULL) {
+ VideoProcessingModule::Destroy(vpm_);
+ }
+ vpm_ = NULL;
+}
+
+TEST_F(VideoProcessingModuleTest, DISABLED_ON_IOS(HandleNullBuffer)) {
+ // TODO(mikhal/stefan): Do we need this one?
+ VideoProcessingModule::FrameStats stats;
+ // Video frame with unallocated buffer.
+ VideoFrame videoFrame;
+
+ EXPECT_EQ(-3, vpm_->GetFrameStats(&stats, videoFrame));
+
+ EXPECT_EQ(-1, vpm_->Deflickering(&videoFrame, &stats));
+
+ EXPECT_EQ(-3, vpm_->BrightnessDetection(videoFrame, stats));
+}
+
+TEST_F(VideoProcessingModuleTest, DISABLED_ON_IOS(HandleBadStats)) {
+ VideoProcessingModule::FrameStats stats;
+ rtc::scoped_ptr<uint8_t[]> video_buffer(new uint8_t[frame_length_]);
+ ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_,
+ source_file_));
+ EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0, width_, height_,
+ 0, kVideoRotation_0, &video_frame_));
+
+ EXPECT_EQ(-1, vpm_->Deflickering(&video_frame_, &stats));
+
+ EXPECT_EQ(-3, vpm_->BrightnessDetection(video_frame_, stats));
+}
+
+TEST_F(VideoProcessingModuleTest, DISABLED_ON_IOS(IdenticalResultsAfterReset)) {
+ VideoFrame video_frame2;
+ VideoProcessingModule::FrameStats stats;
+ // Only testing non-static functions here.
+ rtc::scoped_ptr<uint8_t[]> video_buffer(new uint8_t[frame_length_]);
+ ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_,
+ source_file_));
+ EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0, width_, height_,
+ 0, kVideoRotation_0, &video_frame_));
+ ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
+ ASSERT_EQ(0, video_frame2.CopyFrame(video_frame_));
+ ASSERT_EQ(0, vpm_->Deflickering(&video_frame_, &stats));
+ vpm_->Reset();
+ // Retrieve frame stats again in case Deflickering() has zeroed them.
+ ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame2));
+ ASSERT_EQ(0, vpm_->Deflickering(&video_frame2, &stats));
+ EXPECT_TRUE(CompareFrames(video_frame_, video_frame2));
+
+ ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_,
+ source_file_));
+ EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0, width_, height_,
+ 0, kVideoRotation_0, &video_frame_));
+ ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
+ video_frame2.CopyFrame(video_frame_);
+ ASSERT_EQ(0, vpm_->BrightnessDetection(video_frame_, stats));
+ vpm_->Reset();
+ ASSERT_EQ(0, vpm_->BrightnessDetection(video_frame2, stats));
+ EXPECT_TRUE(CompareFrames(video_frame_, video_frame2));
+}
+
+TEST_F(VideoProcessingModuleTest, DISABLED_ON_IOS(FrameStats)) {
+ VideoProcessingModule::FrameStats stats;
+ rtc::scoped_ptr<uint8_t[]> video_buffer(new uint8_t[frame_length_]);
+ ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_,
+ source_file_));
+ EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0, width_, height_,
+ 0, kVideoRotation_0, &video_frame_));
+
+ EXPECT_FALSE(vpm_->ValidFrameStats(stats));
+ EXPECT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
+ EXPECT_TRUE(vpm_->ValidFrameStats(stats));
+
+ printf("\nFrameStats\n");
+ printf("mean: %u\nnum_pixels: %u\nsubSamplWidth: "
+ "%u\nsumSamplHeight: %u\nsum: %u\n\n",
+ static_cast<unsigned int>(stats.mean),
+ static_cast<unsigned int>(stats.num_pixels),
+ static_cast<unsigned int>(stats.subSamplHeight),
+ static_cast<unsigned int>(stats.subSamplWidth),
+ static_cast<unsigned int>(stats.sum));
+
+ vpm_->ClearFrameStats(&stats);
+ EXPECT_FALSE(vpm_->ValidFrameStats(stats));
+}
+
+TEST_F(VideoProcessingModuleTest, DISABLED_ON_IOS(PreprocessorLogic)) {
+ // Disable temporal sampling (frame dropping).
+ vpm_->EnableTemporalDecimation(false);
+ int resolution = 100;
+ EXPECT_EQ(VPM_OK, vpm_->SetTargetResolution(resolution, resolution, 15));
+ EXPECT_EQ(VPM_OK, vpm_->SetTargetResolution(resolution, resolution, 30));
+ // Disable spatial sampling.
+ vpm_->SetInputFrameResampleMode(kNoRescaling);
+ EXPECT_EQ(VPM_OK, vpm_->SetTargetResolution(resolution, resolution, 30));
+ VideoFrame* out_frame = NULL;
+ // Set rescaling => output frame != NULL.
+ vpm_->SetInputFrameResampleMode(kFastRescaling);
+ PreprocessFrameAndVerify(video_frame_, resolution, resolution, vpm_,
+ &out_frame);
+ // No rescaling=> output frame = NULL.
+ vpm_->SetInputFrameResampleMode(kNoRescaling);
+ EXPECT_EQ(VPM_OK, vpm_->PreprocessFrame(video_frame_, &out_frame));
+ EXPECT_TRUE(out_frame == NULL);
+}
+
+TEST_F(VideoProcessingModuleTest, DISABLED_ON_IOS(Resampler)) {
+ enum { NumRuns = 1 };
+
+ int64_t min_runtime = 0;
+ int64_t total_runtime = 0;
+
+ rewind(source_file_);
+ ASSERT_TRUE(source_file_ != NULL) <<
+ "Cannot read input file \n";
+
+ // CA not needed here
+ vpm_->EnableContentAnalysis(false);
+ // no temporal decimation
+ vpm_->EnableTemporalDecimation(false);
+
+ // Reading test frame
+ rtc::scoped_ptr<uint8_t[]> video_buffer(new uint8_t[frame_length_]);
+ ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_,
+ source_file_));
+ // Using ConvertToI420 to add stride to the image.
+ EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0, width_, height_,
+ 0, kVideoRotation_0, &video_frame_));
+ // Cropped source frame that will contain the expected visible region.
+ VideoFrame cropped_source_frame;
+ cropped_source_frame.CopyFrame(video_frame_);
+
+ for (uint32_t run_idx = 0; run_idx < NumRuns; run_idx++) {
+ // Initiate test timer.
+ const TickTime time_start = TickTime::Now();
+
+ // Init the sourceFrame with a timestamp.
+ video_frame_.set_render_time_ms(time_start.MillisecondTimestamp());
+ video_frame_.set_timestamp(time_start.MillisecondTimestamp() * 90);
+
+ // Test scaling to different sizes: source is of |width|/|height| = 352/288.
+ // Pure scaling:
+ TestSize(video_frame_, video_frame_, width_ / 4, height_ / 4, 25.2, vpm_);
+ TestSize(video_frame_, video_frame_, width_ / 2, height_ / 2, 28.1, vpm_);
+ // No resampling:
+ TestSize(video_frame_, video_frame_, width_, height_, -1, vpm_);
+ TestSize(video_frame_, video_frame_, 2 * width_, 2 * height_, 32.2, vpm_);
+
+ // Scaling and cropping. The cropped source frame is the largest center
+ // aligned region that can be used from the source while preserving aspect
+ // ratio.
+ CropFrame(video_buffer.get(), width_, height_, 0, 56, 352, 176,
+ &cropped_source_frame);
+ TestSize(video_frame_, cropped_source_frame, 100, 50, 24.0, vpm_);
+
+ CropFrame(video_buffer.get(), width_, height_, 0, 30, 352, 225,
+ &cropped_source_frame);
+ TestSize(video_frame_, cropped_source_frame, 400, 256, 31.3, vpm_);
+
+ CropFrame(video_buffer.get(), width_, height_, 68, 0, 216, 288,
+ &cropped_source_frame);
+ TestSize(video_frame_, cropped_source_frame, 480, 640, 32.15, vpm_);
+
+ CropFrame(video_buffer.get(), width_, height_, 0, 12, 352, 264,
+ &cropped_source_frame);
+ TestSize(video_frame_, cropped_source_frame, 960, 720, 32.2, vpm_);
+
+ CropFrame(video_buffer.get(), width_, height_, 0, 44, 352, 198,
+ &cropped_source_frame);
+ TestSize(video_frame_, cropped_source_frame, 1280, 720, 32.15, vpm_);
+
+ // Upsampling to odd size.
+ CropFrame(video_buffer.get(), width_, height_, 0, 26, 352, 233,
+ &cropped_source_frame);
+ TestSize(video_frame_, cropped_source_frame, 501, 333, 32.05, vpm_);
+ // Downsample to odd size.
+ CropFrame(video_buffer.get(), width_, height_, 0, 34, 352, 219,
+ &cropped_source_frame);
+ TestSize(video_frame_, cropped_source_frame, 281, 175, 29.3, vpm_);
+
+ // Stop timer.
+ const int64_t runtime = (TickTime::Now() - time_start).Microseconds();
+ if (runtime < min_runtime || run_idx == 0) {
+ min_runtime = runtime;
+ }
+ total_runtime += runtime;
+ }
+
+ printf("\nAverage run time = %d us / frame\n",
+ static_cast<int>(total_runtime));
+ printf("Min run time = %d us / frame\n\n",
+ static_cast<int>(min_runtime));
+}
+
+void PreprocessFrameAndVerify(const VideoFrame& source,
+ int target_width,
+ int target_height,
+ VideoProcessingModule* vpm,
+ VideoFrame** out_frame) {
+ ASSERT_EQ(VPM_OK, vpm->SetTargetResolution(target_width, target_height, 30));
+ ASSERT_EQ(VPM_OK, vpm->PreprocessFrame(source, out_frame));
+
+ // If no resizing is needed, expect NULL.
+ if (target_width == source.width() && target_height == source.height()) {
+ EXPECT_EQ(NULL, *out_frame);
+ return;
+ }
+
+ // Verify the resampled frame.
+ EXPECT_TRUE(*out_frame != NULL);
+ EXPECT_EQ(source.render_time_ms(), (*out_frame)->render_time_ms());
+ EXPECT_EQ(source.timestamp(), (*out_frame)->timestamp());
+ EXPECT_EQ(target_width, (*out_frame)->width());
+ EXPECT_EQ(target_height, (*out_frame)->height());
+}
+
+void CropFrame(const uint8_t* source_data,
+ int source_width,
+ int source_height,
+ int offset_x,
+ int offset_y,
+ int cropped_width,
+ int cropped_height,
+ VideoFrame* cropped_frame) {
+ cropped_frame->CreateEmptyFrame(cropped_width, cropped_height, cropped_width,
+ (cropped_width + 1) / 2,
+ (cropped_width + 1) / 2);
+ EXPECT_EQ(0,
+ ConvertToI420(kI420, source_data, offset_x, offset_y, source_width,
+ source_height, 0, kVideoRotation_0, cropped_frame));
+}
+
+void TestSize(const VideoFrame& source_frame,
+ const VideoFrame& cropped_source_frame,
+ int target_width,
+ int target_height,
+ double expected_psnr,
+ VideoProcessingModule* vpm) {
+ // Resample source_frame to out_frame.
+ VideoFrame* out_frame = NULL;
+ vpm->SetInputFrameResampleMode(kBox);
+ PreprocessFrameAndVerify(source_frame, target_width, target_height, vpm,
+ &out_frame);
+ if (out_frame == NULL)
+ return;
+ WriteProcessedFrameForVisualInspection(source_frame, *out_frame);
+
+ // Scale |resampled_source_frame| back to the source scale.
+ VideoFrame resampled_source_frame;
+ resampled_source_frame.CopyFrame(*out_frame);
+ PreprocessFrameAndVerify(resampled_source_frame, cropped_source_frame.width(),
+ cropped_source_frame.height(), vpm, &out_frame);
+ WriteProcessedFrameForVisualInspection(resampled_source_frame, *out_frame);
+
+ // Compute PSNR against the cropped source frame and check expectation.
+ double psnr = I420PSNR(&cropped_source_frame, out_frame);
+ EXPECT_GT(psnr, expected_psnr);
+ printf("PSNR: %f. PSNR is between source of size %d %d, and a modified "
+ "source which is scaled down/up to: %d %d, and back to source size \n",
+ psnr, source_frame.width(), source_frame.height(),
+ target_width, target_height);
+}
+
+bool CompareFrames(const webrtc::VideoFrame& frame1,
+ const webrtc::VideoFrame& frame2) {
+ for (int plane = 0; plane < webrtc::kNumOfPlanes; plane ++) {
+ webrtc::PlaneType plane_type = static_cast<webrtc::PlaneType>(plane);
+ int allocated_size1 = frame1.allocated_size(plane_type);
+ int allocated_size2 = frame2.allocated_size(plane_type);
+ if (allocated_size1 != allocated_size2)
+ return false;
+ const uint8_t* plane_buffer1 = frame1.buffer(plane_type);
+ const uint8_t* plane_buffer2 = frame2.buffer(plane_type);
+ if (memcmp(plane_buffer1, plane_buffer2, allocated_size1))
+ return false;
+ }
+ return true;
+}
+
+void WriteProcessedFrameForVisualInspection(const VideoFrame& source,
+ const VideoFrame& processed) {
+ // Skip if writing to files is not enabled.
+ if (!FLAGS_gen_files)
+ return;
+ // Write the processed frame to file for visual inspection.
+ std::ostringstream filename;
+ filename << webrtc::test::OutputPath() << "Resampler_from_" << source.width()
+ << "x" << source.height() << "_to_" << processed.width() << "x"
+ << processed.height() << "_30Hz_P420.yuv";
+ std::cout << "Watch " << filename.str() << " and verify that it is okay."
+ << std::endl;
+ FILE* stand_alone_file = fopen(filename.str().c_str(), "wb");
+ if (PrintVideoFrame(processed, stand_alone_file) < 0)
+ std::cerr << "Failed to write: " << filename.str() << std::endl;
+ if (stand_alone_file)
+ fclose(stand_alone_file);
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/video_processing/main/test/unit_test/video_processing_unittest.h b/webrtc/modules/video_processing/main/test/unit_test/video_processing_unittest.h
new file mode 100644
index 0000000000..4a4fda41e6
--- /dev/null
+++ b/webrtc/modules/video_processing/main/test/unit_test/video_processing_unittest.h
@@ -0,0 +1,47 @@
+/*
+ * 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_MODULES_VIDEO_PROCESSING_MAIN_TEST_UNIT_TEST_VIDEO_PROCESSING_UNITTEST_H
+#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_TEST_UNIT_TEST_VIDEO_PROCESSING_UNITTEST_H
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/modules/video_processing/main/interface/video_processing.h"
+#include "webrtc/system_wrappers/include/trace.h"
+#include "webrtc/test/testsupport/fileutils.h"
+
+namespace webrtc {
+
+class VideoProcessingModuleTest : public ::testing::Test {
+ protected:
+ VideoProcessingModuleTest();
+ virtual void SetUp();
+ virtual void TearDown();
+ static void SetUpTestCase() {
+ Trace::CreateTrace();
+ std::string trace_file = webrtc::test::OutputPath() + "VPMTrace.txt";
+ ASSERT_EQ(0, Trace::SetTraceFile(trace_file.c_str()));
+ }
+ static void TearDownTestCase() {
+ Trace::ReturnTrace();
+ }
+ VideoProcessingModule* vpm_;
+ FILE* source_file_;
+ VideoFrame video_frame_;
+ const int width_;
+ const int half_width_;
+ const int height_;
+ const int size_y_;
+ const int size_uv_;
+ const size_t frame_length_;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_TEST_UNIT_TEST_VIDEO_PROCESSING_UNITTEST_H
diff --git a/webrtc/modules/video_processing/main/test/unit_test/writeYUV420file.m b/webrtc/modules/video_processing/main/test/unit_test/writeYUV420file.m
new file mode 100644
index 0000000000..69a8808338
--- /dev/null
+++ b/webrtc/modules/video_processing/main/test/unit_test/writeYUV420file.m
@@ -0,0 +1,22 @@
+function writeYUV420file(filename, Y, U, V)
+% writeYUV420file(filename, Y, U, V)
+
+fid = fopen(filename,'wb');
+if fid==-1
+ error(['Cannot open file ' filename]);
+end
+
+numFrames=size(Y,3);
+
+for k=1:numFrames
+ % Write luminance
+ fwrite(fid,uint8(Y(:,:,k).'), 'uchar');
+
+ % Write U channel
+ fwrite(fid,uint8(U(:,:,k).'), 'uchar');
+
+ % Write V channel
+ fwrite(fid,uint8(V(:,:,k).'), 'uchar');
+end
+
+fclose(fid);