/* * 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 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; } }