226 lines
7.0 KiB
C++
226 lines
7.0 KiB
C++
|
/*
|
||
|
* 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 "content_metrics_processing.h"
|
||
|
#include "tick_time.h"
|
||
|
#include "module_common_types.h"
|
||
|
#include "video_coding_defines.h"
|
||
|
|
||
|
#include <math.h>
|
||
|
|
||
|
namespace webrtc {
|
||
|
|
||
|
//////////////////////////////////
|
||
|
/// VCMContentMetricsProcessing //
|
||
|
//////////////////////////////////
|
||
|
|
||
|
VCMContentMetricsProcessing::VCMContentMetricsProcessing():
|
||
|
_frameRate(0),
|
||
|
_recAvgFactor(1 / 150.0f), // matched to 30fps
|
||
|
_frameCnt(0),
|
||
|
_prevAvgSizeZeroMotion(0),
|
||
|
_avgSizeZeroMotion(0),
|
||
|
_prevAvgSpatialPredErr(0),
|
||
|
_avgSpatialPredErr(0),
|
||
|
_frameCntForCC(0),
|
||
|
_lastCCpdateTime(0)
|
||
|
{
|
||
|
_globalRecursiveAvg = new VideoContentMetrics();
|
||
|
}
|
||
|
|
||
|
VCMContentMetricsProcessing::~VCMContentMetricsProcessing()
|
||
|
{
|
||
|
delete _globalRecursiveAvg;
|
||
|
}
|
||
|
|
||
|
WebRtc_Word32
|
||
|
VCMContentMetricsProcessing::Reset()
|
||
|
{
|
||
|
_globalRecursiveAvg->Reset();
|
||
|
_frameCnt = 0;
|
||
|
_frameRate = 0;
|
||
|
//_recAvgFactor = 1 / 150.0f; // matched to 30 fps
|
||
|
_prevAvgSizeZeroMotion = 0;
|
||
|
_avgSizeZeroMotion = 0;
|
||
|
_prevAvgSpatialPredErr = 0;
|
||
|
_avgSpatialPredErr = 0;
|
||
|
_frameCntForCC = 0;
|
||
|
|
||
|
return VCM_OK;
|
||
|
}
|
||
|
void
|
||
|
VCMContentMetricsProcessing::UpdateFrameRate(WebRtc_UWord32 frameRate)
|
||
|
{
|
||
|
_frameRate = frameRate;
|
||
|
//Update recursive avg factor
|
||
|
_recAvgFactor = (float) 1000 / ((float)(_frameRate * kQmMinIntervalMs));
|
||
|
|
||
|
}
|
||
|
|
||
|
WebRtc_Word32
|
||
|
VCMContentMetricsProcessing::UpdateContentData(const VideoContentMetrics *contentMetrics)
|
||
|
{
|
||
|
if (contentMetrics == NULL)
|
||
|
{
|
||
|
return VCM_OK;
|
||
|
}
|
||
|
return ProcessContent(contentMetrics);
|
||
|
|
||
|
}
|
||
|
|
||
|
VideoContentMetrics*
|
||
|
VCMContentMetricsProcessing::Data()
|
||
|
{
|
||
|
if (_frameCnt == 0)
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
return _globalRecursiveAvg;
|
||
|
}
|
||
|
|
||
|
WebRtc_UWord32
|
||
|
VCMContentMetricsProcessing::ProcessContent(const VideoContentMetrics *contentMetrics)
|
||
|
{
|
||
|
// update global metric
|
||
|
UpdateGlobalMetric(contentMetrics);
|
||
|
|
||
|
//Update metrics over local window for content change (CC) detection:
|
||
|
//two metrics are used for CC detection: size of zero motion, and spatial prediction error
|
||
|
//Not currently used:
|
||
|
//UpdateLocalMetricCC(contentMetrics->sizeZeroMotion, contentMetrics->spatialPredErr);
|
||
|
|
||
|
return VCM_OK;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
VCMContentMetricsProcessing::ContentChangeCheck()
|
||
|
{
|
||
|
bool result = false;
|
||
|
|
||
|
// Thresholds for bitrate and content change detection
|
||
|
float qmContentChangePercMotion = 0.4f;
|
||
|
float qmContentChangePercSpatial = 0.4f;
|
||
|
|
||
|
WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp();
|
||
|
if ( (now - _lastCCpdateTime) < kCcMinIntervalMs)
|
||
|
{
|
||
|
//keep averaging
|
||
|
return result;
|
||
|
}
|
||
|
else //check for detection and reset
|
||
|
{
|
||
|
//normalize
|
||
|
_avgSizeZeroMotion = _avgSizeZeroMotion / (float)(_frameCntForCC);
|
||
|
_prevAvgSpatialPredErr = _prevAvgSpatialPredErr / (float)(_frameCntForCC);
|
||
|
|
||
|
//check for content change
|
||
|
float diffMotion = fabs(_avgSizeZeroMotion - _prevAvgSizeZeroMotion);
|
||
|
float diffSpatial = fabs(_avgSpatialPredErr -_prevAvgSpatialPredErr);
|
||
|
if ((diffMotion > (_avgSizeZeroMotion * qmContentChangePercMotion)) ||
|
||
|
(diffSpatial > (_prevAvgSpatialPredErr * qmContentChangePercSpatial)))
|
||
|
{
|
||
|
result = true;
|
||
|
}
|
||
|
|
||
|
//copy to previous
|
||
|
_prevAvgSizeZeroMotion = _avgSizeZeroMotion;
|
||
|
_prevAvgSpatialPredErr = _avgSpatialPredErr;
|
||
|
|
||
|
//reset
|
||
|
_avgSizeZeroMotion = 0.;
|
||
|
_avgSpatialPredErr = 0.;
|
||
|
_frameCntForCC = 0;
|
||
|
|
||
|
_lastCCpdateTime = now;
|
||
|
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
//update metrics for content change detection: update is uniform average over soem time window
|
||
|
void VCMContentMetricsProcessing::UpdateLocalMetricCC(float motionVal, float spatialVal)
|
||
|
{
|
||
|
|
||
|
_frameCntForCC += 1;
|
||
|
_avgSizeZeroMotion += motionVal;
|
||
|
_avgSpatialPredErr += spatialVal;
|
||
|
|
||
|
return;
|
||
|
|
||
|
}
|
||
|
void
|
||
|
VCMContentMetricsProcessing::UpdateGlobalMetric(const VideoContentMetrics *contentMetrics)
|
||
|
{
|
||
|
|
||
|
// Threshold for size of zero motion cluster: for updating 3 metrics:
|
||
|
// motion magnitude, cluster distortion, and horizontalness
|
||
|
float nonZeroMvThr = 0.1f;
|
||
|
|
||
|
// first zero and one: take value as is (no motion search in frame zero).
|
||
|
float tmpRecAvgFactor = _recAvgFactor;
|
||
|
if (_frameCnt < 1)
|
||
|
{
|
||
|
_recAvgFactor = 1;
|
||
|
}
|
||
|
|
||
|
_globalRecursiveAvg->motionPredErr = (1 - _recAvgFactor) * _globalRecursiveAvg->motionPredErr +
|
||
|
_recAvgFactor * contentMetrics->motionPredErr;
|
||
|
|
||
|
_globalRecursiveAvg->sizeZeroMotion = (1 - _recAvgFactor) * _globalRecursiveAvg->sizeZeroMotion +
|
||
|
_recAvgFactor * contentMetrics->sizeZeroMotion;
|
||
|
|
||
|
_globalRecursiveAvg->spatialPredErr = (1 - _recAvgFactor) * _globalRecursiveAvg->spatialPredErr +
|
||
|
_recAvgFactor * contentMetrics->spatialPredErr;
|
||
|
|
||
|
_globalRecursiveAvg->spatialPredErrH = (1 - _recAvgFactor) * _globalRecursiveAvg->spatialPredErrH +
|
||
|
_recAvgFactor * contentMetrics->spatialPredErrH;
|
||
|
|
||
|
_globalRecursiveAvg->spatialPredErrV = (1 - _recAvgFactor) * _globalRecursiveAvg->spatialPredErrV +
|
||
|
_recAvgFactor * contentMetrics->spatialPredErrV;
|
||
|
|
||
|
//motionMag metric is derived from NFD (normalized frame difference)
|
||
|
if (kNfdMetric == 1)
|
||
|
{
|
||
|
_globalRecursiveAvg->motionMagnitudeNZ = (1 - _recAvgFactor) * _globalRecursiveAvg->motionMagnitudeNZ +
|
||
|
_recAvgFactor * contentMetrics->motionMagnitudeNZ;
|
||
|
}
|
||
|
|
||
|
if (contentMetrics->sizeZeroMotion > nonZeroMvThr)
|
||
|
{
|
||
|
_globalRecursiveAvg->motionClusterDistortion = (1 - _recAvgFactor) * _globalRecursiveAvg->motionClusterDistortion +
|
||
|
_recAvgFactor *contentMetrics->motionClusterDistortion;
|
||
|
|
||
|
_globalRecursiveAvg->motionHorizontalness = (1 - _recAvgFactor) * _globalRecursiveAvg->motionHorizontalness +
|
||
|
_recAvgFactor * contentMetrics->motionHorizontalness;
|
||
|
|
||
|
//motionMag metric is derived from motion vectors
|
||
|
if (kNfdMetric == 0)
|
||
|
{
|
||
|
_globalRecursiveAvg->motionMagnitudeNZ = (1 - _recAvgFactor) * _globalRecursiveAvg->motionMagnitudeNZ +
|
||
|
_recAvgFactor * contentMetrics->motionMagnitudeNZ;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// update native values:
|
||
|
_globalRecursiveAvg->nativeHeight = contentMetrics->nativeHeight;
|
||
|
_globalRecursiveAvg->nativeWidth = contentMetrics->nativeWidth;
|
||
|
_globalRecursiveAvg->nativeFrameRate = contentMetrics->nativeFrameRate;
|
||
|
|
||
|
if (_frameCnt < 1)
|
||
|
{
|
||
|
_recAvgFactor = tmpRecAvgFactor;
|
||
|
}
|
||
|
_frameCnt++;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
}
|