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/modules/video_coding/codecs/vp8/reference_picture_selection.h"
12
13#include "vpx/vpx_encoder.h"
14#include "vpx/vp8cx.h"
15#include "webrtc/typedefs.h"
16
17namespace webrtc {
18
19ReferencePictureSelection::ReferencePictureSelection()
20    : kRttConfidence(1.33),
21      update_golden_next_(true),
22      established_golden_(false),
23      received_ack_(false),
24      last_sent_ref_picture_id_(0),
25      last_sent_ref_update_time_(0),
26      established_ref_picture_id_(0),
27      last_refresh_time_(0),
28      rtt_(0) {
29}
30
31void ReferencePictureSelection::Init() {
32  update_golden_next_ = true;
33  established_golden_ = false;
34  received_ack_ = false;
35  last_sent_ref_picture_id_ = 0;
36  last_sent_ref_update_time_ = 0;
37  established_ref_picture_id_ = 0;
38  last_refresh_time_ = 0;
39  rtt_ = 0;
40}
41
42void ReferencePictureSelection::ReceivedRPSI(int rpsi_picture_id) {
43  // Assume RPSI is signaled with 14 bits.
44  if ((rpsi_picture_id & 0x3fff) == (last_sent_ref_picture_id_ & 0x3fff)) {
45    // Remote peer has received our last reference frame, switch frame type.
46    received_ack_ = true;
47    established_golden_ = update_golden_next_;
48    update_golden_next_ = !update_golden_next_;
49    established_ref_picture_id_ = last_sent_ref_picture_id_;
50  }
51}
52
53bool ReferencePictureSelection::ReceivedSLI(uint32_t now_ts) {
54  bool send_refresh = false;
55  // Don't send a refresh more than once per round-trip time.
56  // This is to avoid too frequent refreshes, since the receiver
57  // will signal an SLI for every corrupt frame.
58  if (TimestampDiff(now_ts, last_refresh_time_) > rtt_) {
59    send_refresh = true;
60    last_refresh_time_ = now_ts;
61  }
62  return send_refresh;
63}
64
65int ReferencePictureSelection::EncodeFlags(int picture_id, bool send_refresh,
66                                           uint32_t now_ts) {
67  int flags = 0;
68  // We can't refresh the decoder until we have established the key frame.
69  if (send_refresh && received_ack_) {
70    flags |= VP8_EFLAG_NO_REF_LAST;  // Don't reference the last frame
71    if (established_golden_)
72      flags |= VP8_EFLAG_NO_REF_ARF;  // Don't reference the alt-ref frame.
73    else
74      flags |= VP8_EFLAG_NO_REF_GF;  // Don't reference the golden frame
75  }
76
77  // Make sure we don't update the reference frames too often. We must wait long
78  // enough for an RPSI to arrive after the decoder decoded the reference frame.
79  // Ideally that should happen after one round-trip time.
80  // Add a margin defined by |kRttConfidence|.
81  uint32_t update_interval = kRttConfidence * rtt_;
82  if (update_interval < kMinUpdateInterval)
83    update_interval = kMinUpdateInterval;
84  // Don't send reference frame updates until we have an established reference.
85  if (TimestampDiff(now_ts, last_sent_ref_update_time_) > update_interval &&
86      received_ack_) {
87    flags |= VP8_EFLAG_NO_REF_LAST;  // Don't reference the last frame.
88    if (update_golden_next_) {
89      flags |= VP8_EFLAG_FORCE_GF;  // Update the golden reference.
90      flags |= VP8_EFLAG_NO_UPD_ARF;  // Don't update alt-ref.
91      flags |= VP8_EFLAG_NO_REF_GF;  // Don't reference the golden frame.
92    } else {
93      flags |= VP8_EFLAG_FORCE_ARF;  // Update the alt-ref reference.
94      flags |= VP8_EFLAG_NO_UPD_GF;  // Don't update the golden frame.
95      flags |= VP8_EFLAG_NO_REF_ARF;  // Don't reference the alt-ref frame.
96    }
97    last_sent_ref_picture_id_ = picture_id;
98    last_sent_ref_update_time_ = now_ts;
99  } else {
100    // No update of golden or alt-ref. We can therefore freely reference the
101    // established reference frame and the last frame.
102    if (established_golden_)
103      flags |= VP8_EFLAG_NO_REF_ARF;  // Don't reference the alt-ref frame.
104    else
105      flags |= VP8_EFLAG_NO_REF_GF;   // Don't reference the golden frame.
106    flags |= VP8_EFLAG_NO_UPD_GF;  // Don't update the golden frame.
107    flags |= VP8_EFLAG_NO_UPD_ARF;  // Don't update the alt-ref frame.
108  }
109  return flags;
110}
111
112void ReferencePictureSelection::EncodedKeyFrame(int picture_id) {
113  last_sent_ref_picture_id_ = picture_id;
114  received_ack_ = false;
115}
116
117void ReferencePictureSelection::SetRtt(int rtt) {
118  // Convert from milliseconds to timestamp frequency.
119  rtt_ = 90 * rtt;
120}
121
122uint32_t ReferencePictureSelection::TimestampDiff(uint32_t new_ts,
123                                                  uint32_t old_ts) {
124  if (old_ts > new_ts) {
125    // Assuming this is a wrap, doing a compensated subtraction.
126    return (new_ts + (static_cast<int64_t>(1) << 32)) - old_ts;
127  }
128  return new_ts - old_ts;
129}
130
131}  // namespace webrtc
132