1/*
2 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/base/checks.h"
12#include "webrtc/modules/video_processing/include/video_processing.h"
13#include "webrtc/modules/video_processing/video_decimator.h"
14#include "webrtc/system_wrappers/include/tick_util.h"
15
16#define VD_MIN(a, b) ((a) < (b)) ? (a) : (b)
17
18namespace webrtc {
19
20VPMVideoDecimator::VPMVideoDecimator() {
21  Reset();
22}
23
24VPMVideoDecimator::~VPMVideoDecimator() {}
25
26void VPMVideoDecimator::Reset() {
27  overshoot_modifier_ = 0;
28  drop_count_ = 0;
29  keep_count_ = 0;
30  target_frame_rate_ = 30;
31  incoming_frame_rate_ = 0.0f;
32  memset(incoming_frame_times_, 0, sizeof(incoming_frame_times_));
33  enable_temporal_decimation_ = true;
34}
35
36void VPMVideoDecimator::EnableTemporalDecimation(bool enable) {
37  enable_temporal_decimation_ = enable;
38}
39
40void VPMVideoDecimator::SetTargetFramerate(int frame_rate) {
41  RTC_DCHECK(frame_rate);
42  target_frame_rate_ = frame_rate;
43}
44
45bool VPMVideoDecimator::DropFrame() {
46  if (!enable_temporal_decimation_)
47    return false;
48
49  if (incoming_frame_rate_ <= 0)
50    return false;
51
52  const uint32_t incomingframe_rate =
53      static_cast<uint32_t>(incoming_frame_rate_ + 0.5f);
54
55  if (target_frame_rate_ == 0)
56    return true;
57
58  bool drop = false;
59  if (incomingframe_rate > target_frame_rate_) {
60    int32_t overshoot =
61        overshoot_modifier_ + (incomingframe_rate - target_frame_rate_);
62    if (overshoot < 0) {
63      overshoot = 0;
64      overshoot_modifier_ = 0;
65    }
66
67    if (overshoot && 2 * overshoot < (int32_t)incomingframe_rate) {
68      if (drop_count_) {  // Just got here so drop to be sure.
69        drop_count_ = 0;
70        return true;
71      }
72      const uint32_t dropVar = incomingframe_rate / overshoot;
73
74      if (keep_count_ >= dropVar) {
75        drop = true;
76        overshoot_modifier_ = -((int32_t)incomingframe_rate % overshoot) / 3;
77        keep_count_ = 1;
78      } else {
79        keep_count_++;
80      }
81    } else {
82      keep_count_ = 0;
83      const uint32_t dropVar = overshoot / target_frame_rate_;
84      if (drop_count_ < dropVar) {
85        drop = true;
86        drop_count_++;
87      } else {
88        overshoot_modifier_ = overshoot % target_frame_rate_;
89        drop = false;
90        drop_count_ = 0;
91      }
92    }
93  }
94  return drop;
95}
96
97uint32_t VPMVideoDecimator::GetDecimatedFrameRate() {
98  ProcessIncomingframe_rate(TickTime::MillisecondTimestamp());
99  if (!enable_temporal_decimation_) {
100    return static_cast<uint32_t>(incoming_frame_rate_ + 0.5f);
101  }
102  return VD_MIN(target_frame_rate_,
103                static_cast<uint32_t>(incoming_frame_rate_ + 0.5f));
104}
105
106uint32_t VPMVideoDecimator::Inputframe_rate() {
107  ProcessIncomingframe_rate(TickTime::MillisecondTimestamp());
108  return static_cast<uint32_t>(incoming_frame_rate_ + 0.5f);
109}
110
111void VPMVideoDecimator::UpdateIncomingframe_rate() {
112  int64_t now = TickTime::MillisecondTimestamp();
113  if (incoming_frame_times_[0] == 0) {
114    // First no shift.
115  } else {
116    // Shift.
117    for (int i = kFrameCountHistory_size - 2; i >= 0; i--) {
118      incoming_frame_times_[i + 1] = incoming_frame_times_[i];
119    }
120  }
121  incoming_frame_times_[0] = now;
122  ProcessIncomingframe_rate(now);
123}
124
125void VPMVideoDecimator::ProcessIncomingframe_rate(int64_t now) {
126  int32_t num = 0;
127  int32_t nrOfFrames = 0;
128  for (num = 1; num < (kFrameCountHistory_size - 1); num++) {
129    // Don't use data older than 2sec.
130    if (incoming_frame_times_[num] <= 0 ||
131        now - incoming_frame_times_[num] > kFrameHistoryWindowMs) {
132      break;
133    } else {
134      nrOfFrames++;
135    }
136  }
137  if (num > 1) {
138    int64_t diff = now - incoming_frame_times_[num - 1];
139    incoming_frame_rate_ = 1.0;
140    if (diff > 0) {
141      incoming_frame_rate_ = nrOfFrames * 1000.0f / static_cast<float>(diff);
142    }
143  } else {
144    incoming_frame_rate_ = static_cast<float>(nrOfFrames);
145  }
146}
147
148}  // namespace webrtc
149