summaryrefslogtreecommitdiff
path: root/media/webrtc/signaling/src/media-conduit/CodecStatistics.cpp
blob: eb03c0bf809b950a9adef7ead85ac499f21bc2e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "CodecStatistics.h"

#include "CSFLog.h"
#include "mozilla/Telemetry.h"

using namespace mozilla;
using namespace webrtc;

// use the same tag as VideoConduit
static const char* logTag ="WebrtcVideoSessionConduit";

VideoCodecStatistics::VideoCodecStatistics(int channel,
                                           ViECodec* codec) :
  mChannel(channel),
  mSentRawFrames(0),
  mPtrViECodec(codec),
  mEncoderDroppedFrames(0),
  mDecoderDiscardedPackets(0),
  mRegisteredEncode(false),
  mRegisteredDecode(false),
  mReceiveState(kReceiveStateInitial)
#ifdef MOZILLA_INTERNAL_API
  , mRecoveredBeforeLoss(0)
  , mRecoveredLosses(0)
#endif
{
  MOZ_ASSERT(mPtrViECodec);
}

VideoCodecStatistics::~VideoCodecStatistics()
{
  if (mRegisteredEncode) {
    mPtrViECodec->DeregisterEncoderObserver(mChannel);
  }
  if (mRegisteredDecode) {
    mPtrViECodec->DeregisterDecoderObserver(mChannel);
  }
}

void VideoCodecStatistics::Register(bool encoder)
{
  if (encoder && !mRegisteredEncode) {
    mPtrViECodec->RegisterEncoderObserver(mChannel, *this);
    mRegisteredEncode = true;
  } else if (!encoder && !mRegisteredDecode) {
    mPtrViECodec->RegisterDecoderObserver(mChannel, *this);
    mRegisteredDecode = true;
  }
}

void VideoCodecStatistics::OutgoingRate(const int video_channel,
                                        const uint32_t framerate,
                                        const uint32_t bitrate)
{
  unsigned int keyFrames, deltaFrames;
  mPtrViECodec->GetSendCodecStatistics(video_channel, keyFrames, deltaFrames);
  uint32_t dropped = mSentRawFrames - (keyFrames + deltaFrames);
  CSFLogDebug(logTag,
              "encoder statistics - framerate: %u, bitrate: %u, dropped frames: %u",
              framerate, bitrate, dropped);
  mEncoderBitRate.Push(bitrate);
  mEncoderFps.Push(framerate);
  mEncoderDroppedFrames += dropped;
}

void VideoCodecStatistics::IncomingCodecChanged(const int video_channel,
                                                const VideoCodec& video_codec)
{
  CSFLogDebug(logTag,
              "channel %d change codec to \"%s\" ",
              video_channel, video_codec.plName);
}

void VideoCodecStatistics::IncomingRate(const int video_channel,
                                        const unsigned int framerate,
                                        const unsigned int bitrate)
{
  unsigned int discarded = mPtrViECodec->GetDiscardedPackets(video_channel);
  CSFLogDebug(logTag,
      "decoder statistics - framerate: %u, bitrate: %u, discarded packets %u",
      framerate, bitrate, discarded);
  mDecoderBitRate.Push(bitrate);
  mDecoderFps.Push(framerate);
  mDecoderDiscardedPackets += discarded;
}

void VideoCodecStatistics::ReceiveStateChange(const int aChannel,
                                              VideoReceiveState aState)
{
  CSFLogDebug(logTag,"New state for %d: %d (was %d)", aChannel, aState, mReceiveState);
#ifdef MOZILLA_INTERNAL_API
  if (mFirstDecodeTime.IsNull()) {
    mFirstDecodeTime = TimeStamp::Now();
  }
  /*
   * Invalid transitions:
   * WaitingKey -> PreemptiveNACK
   * DecodingWithErrors -> PreemptiveNACK
   */

  switch (mReceiveState) {
    case kReceiveStateNormal:
    case kReceiveStateInitial:
      // in a normal state
      if (aState != kReceiveStateNormal && aState != kReceiveStateInitial) {
        // no longer in a normal state
        if (aState != kReceiveStatePreemptiveNACK) {
          mReceiveFailureTime = TimeStamp::Now();
        }
      } // else Normal<->Initial transition
      break;
    default:
      // not in a normal state
      if (aState == kReceiveStateNormal || aState == kReceiveStateInitial) {

        if (mReceiveState == kReceiveStatePreemptiveNACK) {
          mRecoveredBeforeLoss++;
          CSFLogError(logTag, "Video error avoided by NACK recovery");
        } else if (!mReceiveFailureTime.IsNull()) { // safety
          TimeDuration timeDelta = TimeStamp::Now() - mReceiveFailureTime;
          CSFLogError(logTag, "Video error duration: %u ms",
                      static_cast<uint32_t>(timeDelta.ToMilliseconds()));
          Telemetry::Accumulate(Telemetry::WEBRTC_VIDEO_ERROR_RECOVERY_MS,
                                static_cast<uint32_t>(timeDelta.ToMilliseconds()));

          mRecoveredLosses++; // to calculate losses per minute
          mTotalLossTime += timeDelta;  // To calculate % time in recovery
        }
      } // else non-Normal to different non-normal transition
      break;
  }

#endif

  mReceiveState = aState;
}

void VideoCodecStatistics::EndOfCallStats()
{
#ifdef MOZILLA_INTERNAL_API
  if (!mFirstDecodeTime.IsNull()) {
    TimeDuration callDelta = TimeStamp::Now() - mFirstDecodeTime;
    if (callDelta.ToSeconds() != 0) {
      uint32_t recovered_per_min = mRecoveredBeforeLoss/(callDelta.ToSeconds()/60);
      CSFLogError(logTag, "Video recovery before error per min %u", recovered_per_min);
      Telemetry::Accumulate(Telemetry::WEBRTC_VIDEO_RECOVERY_BEFORE_ERROR_PER_MIN,
                            recovered_per_min);
      uint32_t err_per_min = mRecoveredLosses/(callDelta.ToSeconds()/60);
      CSFLogError(logTag, "Video recovery after error per min %u", err_per_min);
      Telemetry::Accumulate(Telemetry::WEBRTC_VIDEO_RECOVERY_AFTER_ERROR_PER_MIN,
                            err_per_min);
      float percent = (mTotalLossTime.ToSeconds()*100)/callDelta.ToSeconds();
      CSFLogError(logTag, "Video error time percentage %f%%", percent);
      Telemetry::Accumulate(Telemetry::WEBRTC_VIDEO_DECODE_ERROR_TIME_PERMILLE,
                            static_cast<uint32_t>(percent*10));
    }
  }
#endif
}

void VideoCodecStatistics::SentFrame()
{
  mSentRawFrames++;
}

void VideoCodecStatistics::Dump()
{
  Dump(mEncoderBitRate, "encoder bitrate");
  Dump(mEncoderFps, "encoder fps");
  Dump(mDecoderBitRate, "decoder bitrate");
  Dump(mDecoderFps, "decoder fps");
}

void VideoCodecStatistics::Dump(RunningStat& s, const char *name)
{
  CSFLogDebug(logTag,
              "%s, mean: %f, variance: %f, standard deviation: %f",
              name, s.Mean(), s.Variance(), s.StandardDeviation());
}